aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/AUTHORS8
-rw-r--r--lib/ssl/Makefile54
-rw-r--r--lib/ssl/c_src/Makefile26
-rw-r--r--lib/ssl/c_src/Makefile.dist33
-rw-r--r--lib/ssl/c_src/Makefile.in205
-rw-r--r--lib/ssl/c_src/Makefile.win32147
-rw-r--r--lib/ssl/c_src/Makefile.win32.dist45
-rw-r--r--lib/ssl/c_src/debuglog.c251
-rw-r--r--lib/ssl/c_src/debuglog.h50
-rw-r--r--lib/ssl/c_src/esock.c1904
-rw-r--r--lib/ssl/c_src/esock.h273
-rw-r--r--lib/ssl/c_src/esock_openssl.c1213
-rw-r--r--lib/ssl/c_src/esock_osio.c328
-rw-r--r--lib/ssl/c_src/esock_osio.h34
-rw-r--r--lib/ssl/c_src/esock_poll.c222
-rw-r--r--lib/ssl/c_src/esock_poll.h60
-rw-r--r--lib/ssl/c_src/esock_posix_str.c642
-rw-r--r--lib/ssl/c_src/esock_posix_str.h28
-rw-r--r--lib/ssl/c_src/esock_ssl.h110
-rw-r--r--lib/ssl/c_src/esock_utils.c150
-rw-r--r--lib/ssl/c_src/esock_utils.h32
-rw-r--r--lib/ssl/c_src/esock_winsock.h36
-rw-r--r--lib/ssl/doc/html/.gitignore0
-rw-r--r--lib/ssl/doc/man3/.gitignore0
-rw-r--r--lib/ssl/doc/man6/.gitignore0
-rw-r--r--lib/ssl/doc/pdf/.gitignore0
-rw-r--r--lib/ssl/doc/src/Makefile130
-rw-r--r--lib/ssl/doc/src/book.xml51
-rw-r--r--lib/ssl/doc/src/cite.defs112
-rw-r--r--lib/ssl/doc/src/create_certs.xml148
-rw-r--r--lib/ssl/doc/src/fascicules.xml19
-rw-r--r--lib/ssl/doc/src/insidecover.xml14
-rw-r--r--lib/ssl/doc/src/licenses.xml156
-rw-r--r--lib/ssl/doc/src/make.dep30
-rw-r--r--lib/ssl/doc/src/new_ssl.xml678
-rw-r--r--lib/ssl/doc/src/note.gifbin0 -> 1539 bytes
-rw-r--r--lib/ssl/doc/src/notes.xml1132
-rw-r--r--lib/ssl/doc/src/pkix_certs.xml253
-rw-r--r--lib/ssl/doc/src/refman.xml51
-rw-r--r--lib/ssl/doc/src/release_notes.xml49
-rw-r--r--lib/ssl/doc/src/remember.xml83
-rw-r--r--lib/ssl/doc/src/ssl.fig10
-rw-r--r--lib/ssl/doc/src/ssl.xml728
-rw-r--r--lib/ssl/doc/src/ssl_app.xml182
-rw-r--r--lib/ssl/doc/src/ssl_distribution.xml235
-rw-r--r--lib/ssl/doc/src/ssl_protocol.xml349
-rw-r--r--lib/ssl/doc/src/usersguide.xml55
-rw-r--r--lib/ssl/doc/src/using_ssl.xml113
-rw-r--r--lib/ssl/doc/src/warning.gifbin0 -> 1498 bytes
-rw-r--r--lib/ssl/ebin/.gitignore0
-rw-r--r--lib/ssl/examples/certs/Makefile24
-rw-r--r--lib/ssl/examples/certs/Makefile.in80
-rw-r--r--lib/ssl/examples/certs/ebin/.gitignore0
-rw-r--r--lib/ssl/examples/certs/rnd/RANDbin0 -> 512 bytes
-rw-r--r--lib/ssl/examples/certs/src/make_certs.erl261
-rw-r--r--lib/ssl/examples/ebin/.gitignore0
-rw-r--r--lib/ssl/examples/src/Makefile78
-rw-r--r--lib/ssl/examples/src/client_server.erl85
-rw-r--r--lib/ssl/include/.gitignore0
-rw-r--r--lib/ssl/info2
-rw-r--r--lib/ssl/internal_doc/ssl-implementation.txt52
-rw-r--r--lib/ssl/pkix/Makefile121
-rw-r--r--lib/ssl/pkix/OTP-PKIX.asn1config2
-rw-r--r--lib/ssl/pkix/OTP-PKIX.set.asn6
-rwxr-xr-xlib/ssl/pkix/PKCS-1.asn154
-rw-r--r--lib/ssl/pkix/PKIX1Algorithms88.asn1274
-rw-r--r--lib/ssl/pkix/PKIX1Algorithms88.hrl94
-rw-r--r--lib/ssl/pkix/PKIX1Explicit88.asn1619
-rw-r--r--lib/ssl/pkix/PKIX1Explicit88.hrl163
-rw-r--r--lib/ssl/pkix/PKIX1Implicit88.asn1349
-rw-r--r--lib/ssl/pkix/PKIX1Implicit88.hrl93
-rw-r--r--lib/ssl/pkix/PKIXAttributeCertificate.asn1189
-rw-r--r--lib/ssl/pkix/PKIXAttributeCertificate.hrl64
-rw-r--r--lib/ssl/pkix/README49
-rw-r--r--lib/ssl/pkix/SSL-PKIX.asn1704
-rw-r--r--lib/ssl/pkix/mk_ssl_pkix_oid.erl94
-rw-r--r--lib/ssl/pkix/prebuild.skip5
-rw-r--r--lib/ssl/priv/bin/.gitignore0
-rw-r--r--lib/ssl/priv/obj/.gitignore0
-rw-r--r--lib/ssl/src/Makefile142
-rw-r--r--lib/ssl/src/inet_ssl_dist.erl449
-rw-r--r--lib/ssl/src/ssl.app.src41
-rw-r--r--lib/ssl/src/ssl.appup.src21
-rw-r--r--lib/ssl/src/ssl.erl841
-rw-r--r--lib/ssl/src/ssl_alert.erl107
-rw-r--r--lib/ssl/src/ssl_alert.hrl97
-rw-r--r--lib/ssl/src/ssl_app.erl41
-rw-r--r--lib/ssl/src/ssl_base64.erl129
-rw-r--r--lib/ssl/src/ssl_broker.erl1188
-rw-r--r--lib/ssl/src/ssl_broker_int.hrl38
-rw-r--r--lib/ssl/src/ssl_broker_sup.erl46
-rw-r--r--lib/ssl/src/ssl_certificate.erl156
-rw-r--r--lib/ssl/src/ssl_certificate_db.erl219
-rw-r--r--lib/ssl/src/ssl_cipher.erl784
-rw-r--r--lib/ssl/src/ssl_cipher.hrl253
-rw-r--r--lib/ssl/src/ssl_connection.erl1704
-rw-r--r--lib/ssl/src/ssl_connection_sup.erl60
-rw-r--r--lib/ssl/src/ssl_debug.erl99
-rw-r--r--lib/ssl/src/ssl_debug.hrl39
-rw-r--r--lib/ssl/src/ssl_handshake.erl917
-rw-r--r--lib/ssl/src/ssl_handshake.hrl200
-rw-r--r--lib/ssl/src/ssl_int.hrl99
-rw-r--r--lib/ssl/src/ssl_internal.hrl91
-rw-r--r--lib/ssl/src/ssl_manager.erl340
-rw-r--r--lib/ssl/src/ssl_pem.erl147
-rw-r--r--lib/ssl/src/ssl_pkix.erl307
-rw-r--r--lib/ssl/src/ssl_pkix.hrl81
-rw-r--r--lib/ssl/src/ssl_prim.erl173
-rw-r--r--lib/ssl/src/ssl_record.erl577
-rw-r--r--lib/ssl/src/ssl_record.hrl170
-rw-r--r--lib/ssl/src/ssl_server.erl1378
-rw-r--r--lib/ssl/src/ssl_session.erl172
-rw-r--r--lib/ssl/src/ssl_session_cache.erl124
-rw-r--r--lib/ssl/src/ssl_session_cache_api.erl37
-rw-r--r--lib/ssl/src/ssl_ssl2.erl37
-rw-r--r--lib/ssl/src/ssl_ssl3.erl286
-rw-r--r--lib/ssl/src/ssl_sup.erl100
-rw-r--r--lib/ssl/src/ssl_tls1.erl251
-rw-r--r--lib/ssl/vsn.mk42
119 files changed, 25874 insertions, 0 deletions
diff --git a/lib/ssl/AUTHORS b/lib/ssl/AUTHORS
new file mode 100644
index 0000000000..57570c93f1
--- /dev/null
+++ b/lib/ssl/AUTHORS
@@ -0,0 +1,8 @@
+Original authors
+
+Ingela Anderton Andin - ssl-4.0 (alfa in ssl-3.10)
+Dan Gudmundsson - ssl-4.0 (alfa in ssl-3.10)
+Ingela Anderton Andin - ssl-4.0 (alfa in ssl-3.9)
+Jakob Cederlund - ssl-4.0 (alfa in ssl-3.9)
+Peter H�gfeldt - first version
+
diff --git a/lib/ssl/Makefile b/lib/ssl/Makefile
new file mode 100644
index 0000000000..a3dec8da38
--- /dev/null
+++ b/lib/ssl/Makefile
@@ -0,0 +1,54 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-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
+
+#
+# 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
+
+include vsn.mk
+VSN = $(SSL_VSN)
+
+SPECIAL_TARGETS =
+
+#
+# Default Subdir Targets
+#
+include $(ERL_TOP)/make/otp_subdir.mk
+
+
diff --git a/lib/ssl/c_src/Makefile b/lib/ssl/c_src/Makefile
new file mode 100644
index 0000000000..52d9140153
--- /dev/null
+++ b/lib/ssl/c_src/Makefile
@@ -0,0 +1,26 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-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%
+#
+
+#
+
+#
+# Invoke with GNU make or clearmake -C gnu.
+#
+
+include $(ERL_TOP)/make/run_make.mk
diff --git a/lib/ssl/c_src/Makefile.dist b/lib/ssl/c_src/Makefile.dist
new file mode 100644
index 0000000000..2468468921
--- /dev/null
+++ b/lib/ssl/c_src/Makefile.dist
@@ -0,0 +1,33 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-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%
+#
+
+# Makefile for SSL on Unix
+#
+# Placed in obj directory.
+#
+CC = gcc
+
+BINDIR = %BINDIR%
+LIBS = %LIBS%
+SSL_LIBDIR = %SSL_LIBDIR%
+OBJS = %OBJS%
+
+$(BINDIR)/ssl_esock: $(OBJS)
+ $(CC) -L$(SSL_LIBDIR) -Wl,-R$(SSL_LIBDIR) -o $@ $^ \
+ $(LIBS) -lssl -lcrypto
diff --git a/lib/ssl/c_src/Makefile.in b/lib/ssl/c_src/Makefile.in
new file mode 100644
index 0000000000..bd1b2f9375
--- /dev/null
+++ b/lib/ssl/c_src/Makefile.in
@@ -0,0 +1,205 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-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%
+#
+
+#
+# Makefile only for Unix and Win32/Cygwin.
+#
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+# ----------------------------------------------------
+# SSL locations and include options from configure
+# ----------------------------------------------------
+SSL_LIBDIR = @SSL_LIBDIR@
+SSL_INCLUDE = @SSL_INCLUDE@
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(SSL_VSN)
+
+# ----------------------------------------------------
+# Commands
+# ----------------------------------------------------
+CC = @CC@
+LD = @LD@
+SHELL = /bin/sh
+LIBS = @LIBS@
+PLAIN_CFLAGS = @CFLAGS@
+
+# ----------------------------------------------------
+# Includes and libs
+# ----------------------------------------------------
+
+ALL_CFLAGS = @WFLAGS@ @CFLAGS@ @DEFS@ $(TYPE_FLAGS)
+TARGET = @host@
+
+ifeq ($(TYPE),debug)
+TYPEMARKER = .debug
+TYPE_FLAGS = -g -DDEBUG @DEBUG_FLAGS@
+else
+TYPEMARKER =
+TYPE_FLAGS = -O2
+endif
+
+PRIVDIR = ../priv
+BINDIR = $(PRIVDIR)/bin/$(TARGET)
+OBJDIR = $(PRIVDIR)/obj/$(TARGET)
+
+# ----------------------------------------------------
+# File suffixes
+# ----------------------------------------------------
+exe = @EXEEXT@
+obj = .@OBJEXT@
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/ssl-$(VSN)
+
+# ----------------------------------------------------
+# Common Macros
+# ----------------------------------------------------
+OBJS = $(OBJDIR)/esock$(obj) \
+ $(OBJDIR)/debuglog$(obj) \
+ $(OBJDIR)/esock_poll$(obj) \
+ $(OBJDIR)/esock_osio$(obj) \
+ $(OBJDIR)/esock_utils$(obj) \
+ $(OBJDIR)/esock_posix_str$(obj) \
+ $(OBJDIR)/esock_openssl$(obj)
+
+PORT_PROGRAM = $(BINDIR)/ssl_esock$(exe)
+
+SKIP_BUILDING_BINARIES := false
+
+# Try to be BC for R10
+ifeq ($(findstring @SSL_,@SSL_DYNAMIC_ONLY@),@SSL_)
+DYNAMIC_CRYPTO_LIB=yes
+else
+DYNAMIC_CRYPTO_LIB=@SSL_DYNAMIC_ONLY@
+endif
+
+
+ifeq ($(DYNAMIC_CRYPTO_LIB),yes)
+
+ifneq ($(findstring win32,$(TARGET)),win32)
+SSL_MAKEFILE = $(OBJDIR)/Makefile
+else
+SSL_MAKEFILE =
+endif
+
+CC_R_FLAG=@CFLAG_RUNTIME_LIBRARY_PATH@
+ifeq ($(findstring @,$(CC_R_FLAG)),@)
+# Old erts configure used which hasn't replaced @CFLAG_RUNTIME_LIBRARY_PATH@;
+# we try our best here instead...
+
+ifeq ($(findstring darwin,$(TARGET)),darwin) # darwin: no flag
+CC_R_FLAG =
+else
+ifeq ($(findstring osf,$(TARGET)),osf) # osf1: -Wl,-rpath,
+CC_R_FLAG = -Wl,-rpath,
+else # Default: -Wl,-R
+CC_R_FLAG = -Wl,-R
+endif
+endif
+endif
+
+ifeq ($(strip $(CC_R_FLAG)),)
+CC_R_OPT =
+else
+CC_R_OPT = $(CC_R_FLAG)$(SSL_LIBDIR)
+endif
+
+SSL_LINK_LIB=-L$(SSL_LIBDIR) -lssl -lcrypto
+else
+# not dynamic crypto lib (default from R11B-5)
+NEED_KERBEROS=@SSL_LINK_WITH_KERBEROS@
+NEED_ZLIB=@SSL_LINK_WITH_ZLIB@
+SSL_MAKEFILE =
+CC_R_OPT =
+SSL_LINK_LIB = $(SSL_LIBDIR)/libssl.a $(SSL_LIBDIR)/libcrypto.a
+ifeq ($(NEED_KERBEROS),yes)
+SSL_LINK_LIB += @STATIC_KERBEROS_LIBS@
+endif
+ifeq ($(NEED_ZLIB),yes)
+SSL_LINK_LIB += @STATIC_ZLIB_LIBS@
+endif
+endif
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(OBJDIR) $(BINDIR) $(OBJS) $(PORT_PROGRAM) $(SSL_MAKEFILE)
+
+$(OBJDIR):
+ -@mkdir -p $(OBJDIR)
+
+$(BINDIR):
+ -@mkdir -p $(BINDIR)
+
+$(OBJDIR)/esock_openssl$(obj): esock_openssl.c
+ $(CC) -c -o $@ $(ALL_CFLAGS) $(SSL_INCLUDE) $<
+
+$(OBJDIR)/%$(obj): %.c
+ $(CC) -c -o $@ $(ALL_CFLAGS) $<
+
+# Unix
+$(BINDIR)/ssl_esock: $(OBJS)
+ $(CC) $(CC_R_OPT) $(PLAIN_CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) $(SSL_LINK_LIB)
+
+# Win32/Cygwin
+$(BINDIR)/ssl_esock.exe: $(OBJS)
+ $(LD) -L$(SSL_LIBDIR) -o $@ $^ -lwsock32 -llibeay32 -lssleay32
+
+# Unix only, and only when linking statically
+$(SSL_MAKEFILE):
+ sed -e "s;%BINDIR%;../../bin/$(TARGET);" \
+ -e "s;%SSL_LIBDIR%;$(SSL_LIBDIR);" \
+ -e "s;%OBJS;$(OBJS);" \
+ -e "s;%LIBS%;$(LIBS);" ./Makefile.dist \
+ > $(OBJDIR)/Makefile
+
+
+clean:
+ rm -f $(PORT_PROGRAM) $(OBJS) core *~ $(SSL_MAKEFILE)
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/priv/bin
+ $(INSTALL_PROGRAM) $(PORT_PROGRAM) $(RELSYSDIR)/priv/bin
+ifneq ($(SSL_MAKEFILE),)
+ $(INSTALL_DIR) $(RELSYSDIR)/priv/obj
+ $(INSTALL_DATA) $(OBJS) $(RELSYSDIR)/priv/obj
+ sed -e "s;%BINDIR%;../bin;" \
+ -e "s;%SSL_LIBDIR%;$(SSL_LIBDIR);" \
+ -e "s;%OBJS;$(OBJS);" \
+ -e "s;%LIBS%;$(LIBS);" ./Makefile.dist \
+ > $(RELSYSDIR)/priv/obj/Makefile
+endif
+
+release_docs_spec:
+
diff --git a/lib/ssl/c_src/Makefile.win32 b/lib/ssl/c_src/Makefile.win32
new file mode 100644
index 0000000000..668cd2a28d
--- /dev/null
+++ b/lib/ssl/c_src/Makefile.win32
@@ -0,0 +1,147 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-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%
+#
+
+#
+# SSL - Makefile for Windows NT
+#
+# It is assumed that the following environment variables have been set:
+#
+# INCLUDE X:\MSDEV\INCLUDE
+# LIB X:\MSDEV\LIB
+#
+# so that standard include files, and the socket library can be found.
+#
+# When ssl_esock.exe is run, the PATH environment variable must contain
+# the name of a directory that contains ssleay32.dll and libeay32.dll,
+# and windows socket dll.
+#
+
+# Roots
+!ifndef OPENSSL_ROOT
+! error "Makefile.win32: ssl: OPENSSL_ROOT not set"
+!endif
+
+TARGET = win32
+
+BINDIR = ..\priv\bin\$(TARGET)
+OBJDIR = ..\priv\obj\$(TARGET)
+
+!if !exist($(BINDIR))
+! if [mkdir $(BINDIR)]
+! error "SSL: cannot create BINDIR"
+! endif
+!endif
+
+!if !exist($(OBJDIR))
+! if [mkdir $(OBJDIR)]
+! error "SSL: cannot create OBJDIR"
+! endif
+!endif
+
+# Includes
+#
+OPENSSL_INCLUDE = $(OPENSSL_ROOT)\inc32
+
+INCLUDES = /I. /I$(OPENSSL_INCLUDE)
+
+# Libraries
+#
+OPENSSL_LIBDIR = $(OPENSSL_ROOT)\out32dll
+OPENSSL_LIBS = \
+ $(OPENSSL_LIBDIR)\ssleay32.lib \
+ $(OPENSSL_LIBDIR)\libeay32.lib
+
+!ifdef ESOCK_WINSOCK2
+WINSOCK_LIB = ws2_32.lib
+DEFS = -DESOCK_WINSOCK2
+!else
+WINSOCK_LIB = wsock32.lib
+!endif
+
+# Compiler options
+#
+# NOTE: Size of fd_set is set in esock_winsock.h but can be overridden
+# with a -D option here.
+#
+OPTS = /MDd /G5 /Ox /O2 /Ob2 /Z7
+DEFS = -D__WIN32__ -DWIN32 $(DEFS)
+CFLAGS = $(INCLUDES) /nologo $(OPTS) $(DEFS)
+
+# Object files
+#
+SSL_BASE_OBJS = \
+ $(OBJDIR)\esock.obj \
+ $(OBJDIR)\debuglog.obj \
+ $(OBJDIR)\esock_poll$(obj) \
+ $(OBJDIR)\esock_osio.obj \
+ $(OBJDIR)\esock_utils.obj \
+ $(OBJDIR)\esock_posix_str.obj
+
+OPENSSL_OBJS = \
+ $(OBJDIR)\esock_openssl.obj
+
+#
+# Targets
+#
+
+all: $(SSL_BASE_OBJS) $(OPENSSL_OBJS) $(BINDIR)\ssl_esock.exe
+
+clean:
+ del $(BINDIR)\*.exe
+ del $(OBJDIR)\*.obj
+
+# Inference rule .c.obj:
+#
+{.}.c{$(OBJDIR)}.obj:
+ $(CC) $(CFLAGS) /c /Fo$@ $(*B).c
+
+# Binary
+#
+$(BINDIR)\ssl_esock.exe: $(SSL_BASE_OBJS) $(OPENSSL_OBJS)
+ $(CC) /nologo $(SSL_BASE_OBJS) $(OPENSSL_OBJS) $(OPENSSL_LIBS) \
+ $(WINSOCK_LIB) /Fe$(BINDIR)\ssl_esock.exe
+
+
+
+# Dependencies
+#
+$(OBJDIR)\esock.o: esock.h debuglog.h esock_ssl.h esock_osio.h \
+ esock_utils.h esock_winsock.h
+$(OBJDIR)\debuglog.o: debuglog.h esock_ssl.h esock_utils.h
+$(OBJDIR)\esock_osio.o: esock_osio.h esock.h debuglog.h esock_utils.h \
+ esock_winsock.h
+$(OBJDIR)\esock_utils.o: esock_utils.h
+$(OBJDIR)\esock_posix_str.o: esock_posix_str.h esock_winsock.h
+
+$(OBJDIR)\esock_openssl.o: esock.h esock_ssl.h debuglog.h esock_utils.h \
+ $(OPENSSL_INCLUDE)\crypto.h \
+ $(OPENSSL_INCLUDE)\ssl.h \
+ $(OPENSSL_INCLUDE)\err.h
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/ssl/c_src/Makefile.win32.dist b/lib/ssl/c_src/Makefile.win32.dist
new file mode 100644
index 0000000000..8510c44e08
--- /dev/null
+++ b/lib/ssl/c_src/Makefile.win32.dist
@@ -0,0 +1,45 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-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%
+#
+
+# Makefile.win32.dist for SSL
+#
+# To be placed in obj directory.
+#
+
+CC = cl
+
+BINDIR = %BINDIR%
+
+OPENSSL_LIBS = \
+ $(BINDIR)\ssleay32.lib \
+ $(BINDIR)\libeay32.lib
+
+WINSOCK_LIB = ws2_32.lib
+
+SSL_BASE_OBJS = esock.obj debuglog.obj esock_osio.obj esock_utils.obj \
+ esock_posix_str.obj
+
+OPENSSL_OBJS = esock_openssl.obj
+
+$(BINDIR)\ssl_esock.exe: $(SSL_BASE_OBJS) $(OPENSSL_OBJS)
+ $(CC) /nologo $(SSL_BASE_OBJS) $(OPENSSL_OBJS) $(OPENSSL_LIBS) \
+ $(WINSOCK_LIB) /Fe$(BINDIR)\ssl_esock.exe
+
+
+
diff --git a/lib/ssl/c_src/debuglog.c b/lib/ssl/c_src/debuglog.c
new file mode 100644
index 0000000000..e2e55df4b2
--- /dev/null
+++ b/lib/ssl/c_src/debuglog.c
@@ -0,0 +1,251 @@
+/*<copyright>
+ * <year>1999-2008</year>
+ * <holder>Ericsson AB, All Rights Reserved</holder>
+ *</copyright>
+ *<legalnotice>
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson AB.
+ *</legalnotice>
+ */
+/*
+ * Purpose: Various routines for debug printouts and logs.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include "debuglog.h"
+#include "esock_utils.h"
+
+#ifndef __WIN32__
+static char tr_format_buf[256];
+static char *tr_format(const char *format);
+static int vfprintclistf(FILE *fp, const char *format, va_list args);
+#endif
+
+int debug = 0;
+int debugmsg = 0;
+FILE *ssllogfp = NULL;
+FILE *__locallogfp = NULL;
+
+void open_ssllog(char *path)
+{
+ ssllogfp = openlog(path);
+}
+
+void close_ssllog(void)
+{
+ if (ssllogfp)
+ closelog(ssllogfp);
+}
+
+FILE *openlog(char *s)
+{
+ FILE *fp;
+ time_t t = time(NULL);
+
+ if ((fp = fopen(s, "a"))) {
+ setbuf(fp, NULL);
+ fprintf(fp, "===== Opened [%s] %s", s, ctime(&t));
+ }
+ return fp;
+}
+
+void closelog(FILE *fp)
+{
+ time_t t = time(NULL);
+
+ if (fp) {
+ fprintf(fp, "Closed %s", ctime(&t));
+ fclose(fp);
+ }
+}
+
+int __debugprintf(const char *format, ...)
+{
+ va_list args;
+ int ret;
+#ifndef __WIN32__
+ char *newformat;
+
+ va_start(args, format);
+ newformat = tr_format(format);
+ ret = vfprintf(stderr, newformat, args);
+ if (newformat != format && newformat != tr_format_buf)
+ esock_free(newformat);
+#else
+ va_start(args, format);
+ ret = vfprintf(stderr, format, args);
+#endif
+ va_end(args);
+ if (ssllogfp) {
+ va_start(args, format);
+ vfprintf(ssllogfp, format, args);
+ va_end(args);
+ }
+ return ret;
+}
+
+int __debugprintclistf(const char *format, ...)
+{
+ va_list args;
+ int ret;
+#ifndef __WIN32__
+ char *newformat;
+
+ va_start(args, format);
+ newformat = tr_format(format);
+ ret = vfprintclistf(stderr, newformat, args);
+ if (newformat != format && newformat != tr_format_buf)
+ esock_free(newformat);
+#else
+ va_start(args, format);
+ ret = vfprintclistf(stderr, format, args);
+#endif
+ if (ssllogfp)
+ vfprintclistf(ssllogfp, format, args);
+ va_end(args);
+ return ret;
+}
+
+int __debuglogf(const char *format, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, format);
+ ret = vfprintf(__locallogfp, format, args);
+ va_end(args);
+ return ret;
+}
+
+#ifndef __WIN32__
+
+/* Insert `\r' before each `\n' i format */
+static char *tr_format(const char *format)
+{
+ char *newformat, *s, *t;
+ int len;
+
+ len = strlen(format);
+ if ((newformat = (len > 127) ? esock_malloc(len) : tr_format_buf)) {
+ for (s = (char *)format, t = newformat; *s; *t++ = *s++)
+ if (*s == '\n')
+ *t++ = '\r';
+ *t = '\0';
+ } else
+ newformat = (char *)format;
+ return newformat;
+}
+
+#endif
+
+/* This function is for printing arrays of characters with formats
+ * %FPa or %FPb, where F and P are the ordinary specifiers for
+ * field width and precision, respectively.
+ *
+ * The conversion specifier `a' implies hex-string output, while
+ * the `b' specifier provides character output (for non-printable
+ * characters a `.' is written.
+ *
+ * The F specifier contains the width for each character. The
+ * P specifier tells how many characters to print.
+ *
+ * Example: Suppose we have a function myprintf(char *format, ...)
+ * that calls our vfprintclistf(), and that
+ *
+ * char buf[] = "h\r\n";
+ * len = 3;
+ *
+ * Then
+ *
+ * myprintf("%.2b", buf) prints "h."
+ * myprintf("%2.3b", buf) prints "h . . "
+ * myprintf("%3.*a", len, buf) prints "68 0d 0a"
+ *
+ */
+
+static int vfprintclistf(FILE *fp, const char *format, va_list args)
+{
+
+ int i, len, width, prec, written = 0;
+ char *s, *prevs, *fstart;
+ unsigned char *buf;
+
+ if (!format || !*format)
+ return 0;
+
+ /* %{[0-9]*|\*}{.{[0-9]*|\*}{a|b} */
+
+ prevs = (char *)format; /* format is const */
+ s = strchr(format, '%');
+ while (s && *s) {
+ if (s - prevs > 0)
+ written += fprintf(fp, "%.*s", s - prevs, prevs);
+ width = prec = 0;
+ fstart = s;
+ s++;
+ if (*s != '%') { /* otherwise it is not a format */
+ if (*s == '*') { /* width in arg */
+ s++;
+ width = va_arg(args, int);
+ } else if ((len = strspn(s, "0123456789"))) { /* const width */
+ width = atoi(s);
+ s += len;
+ } else
+ width = 0;
+ if (*s == '.') { /* precision specified */
+ s++;
+ if (*s == '*') { /* precision in arg */
+ s++;
+ prec = va_arg(args, int);
+ } else if ((len = strspn(s, "0123456789"))) { /* const prec */
+ prec = atoi(s);
+ s += len;
+ } else /* no precision value, defaults to zero */
+ prec = 0;
+ } else
+ prec = 0; /* no precision defaults to zero */
+ if (*s == 'a' || *s == 'b') { /* only valid specifiers */
+ buf = va_arg(args, unsigned char *);
+ if (*s == 'a') {
+ for (i = 0; i < prec; i++)
+ written += fprintf(fp, "%*.2x", width, buf[i]);
+ }else if (*s == 'b') {
+ for (i = 0; i < prec; i++) {
+ if (isprint(buf[i]))
+ written += fprintf(fp, "%*c", width, buf[i]);
+ else
+ written += fprintf(fp, "%*c", width, '.');
+ }
+ }
+ } else {
+ fprintf(stderr, "fprintclistf: format \"%s\" invalid.\n",
+ format);
+ va_end(args);
+ return written;
+ }
+ }
+ s++;
+ /* Now s points to the next character after the format */
+ prevs = s;
+ s = strchr(s, '%');
+ }
+ if (format + strlen(format) + 1 - prevs > 0)
+ written += fprintf(fp, "%s", prevs);
+ return written;
+}
+
diff --git a/lib/ssl/c_src/debuglog.h b/lib/ssl/c_src/debuglog.h
new file mode 100644
index 0000000000..5699e6b495
--- /dev/null
+++ b/lib/ssl/c_src/debuglog.h
@@ -0,0 +1,50 @@
+/*<copyright>
+ * <year>1998-2008</year>
+ * <holder>Ericsson AB, All Rights Reserved</holder>
+ *</copyright>
+ *<legalnotice>
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson AB.
+ *</legalnotice>
+ */
+/*
+ * Purpose: Debug functions and macros.
+ *
+ */
+
+#ifndef __DEBUGLOG_H_
+#define __DEBUGLOG_H_
+
+#include <stdio.h>
+#include "esock_ssl.h"
+
+#define DEBUGF(x) if (debug) __debugprintf x;
+#define DEBUGMSGF(x) if (debugmsg) __debugprintclistf x;
+#define LOGF(fp, x) if (fp) { __locallogfp = fp; __debuglogf x; }
+#define SSLDEBUGF() if (debug) { esock_ssl_print_errors_fp(stderr); \
+ if (ssllogfp) esock_ssl_print_errors_fp(ssllogfp); }
+
+int debug;
+int debugmsg;
+FILE *ssllogfp;
+FILE *__locallogfp;
+
+void open_ssllog(char *path);
+void close_ssllog(void);
+FILE *openlog(char *);
+void closelog(FILE *);
+int __debugprintf(const char *, ...);
+int __debugprintclistf(const char *, ...);
+int __debuglogf(const char *, ...);
+
+#endif
diff --git a/lib/ssl/c_src/esock.c b/lib/ssl/c_src/esock.c
new file mode 100644
index 0000000000..78d08f7c29
--- /dev/null
+++ b/lib/ssl/c_src/esock.c
@@ -0,0 +1,1904 @@
+/*<copyright>
+ * <year>1999-2008</year>
+ * <holder>Ericsson AB, All Rights Reserved</holder>
+ *</copyright>
+ *<legalnotice>
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson AB.
+ *</legalnotice>
+ */
+
+/*
+ * Purpose: Implementation of Secure Socket Layer (SSL).
+ *
+ * This is an "SSL proxy" for Erlang in the form of a port
+ * program.
+ *
+ * The implementation has borrowed somewhat from the original
+ * implementation of `socket' by Claes Wikstr�m, and the former
+ * implementation of `ssl_socket' by Helen Ariyan.
+ *
+ * All I/O is now non-blocking.
+ *
+ * When a connection (cp) is in the state JOINED we have the following
+ * picture:
+ *
+ * proxy->fd fd
+ * | |
+ * proxy->eof | --------> wq -----------> | bp
+ * | |
+ * Erlang | | SSL
+ * | |
+ * proxy->bp | <------ proxy->wq --------- | eof
+ * | |
+ *
+ * We read from Erlang (proxy->fd) and write to SSL (fd); and read from
+ * SSL (fd) and write to Erlang (proxy->fd).
+ *
+ * The variables bp (broken pipe) and eof (end of file) take the
+ * values 0 and 1.
+ *
+ * What has been read and cannot be immediately written is put in a
+ * write queue (wq). A wq is emptied before reads are continued, which
+ * means that at most one chunk that is read can be in a wq.
+ *
+ * The proxy-to-ssl part of a cp is valid iff
+ *
+ * !bp && (wq.len > 0 || !proxy->eof).
+ *
+ * The ssl-to-proxy part of a cp is valid iff
+ *
+ * !proxy->bp && (proxy->wq.len > 0 || !eof).
+ *
+ * The connection is valid if any of the above parts are valid, i.e.
+ * invalid if both parts are invalid.
+ *
+ * Every SELECT_TIMEOUT second we try to write to those file
+ * descriptors that have non-empty wq's (the only way to detect that a
+ * far end has gone away is to write to it).
+ *
+ * STATE TRANSITIONS
+ *
+ * Below (*) means that the corresponding file descriptor is published
+ * (i.e. kwown outside this port program) when the state is entered,
+ * and thus cannot be closed without synchronization with the
+ * ssl_server.
+ *
+ * Listen:
+ *
+ * STATE_NONE ---> (*) PASSIVE_LISTENING <---> ACTIVE_LISTENING
+ *
+ * Accept:
+ *
+ * STATE_NONE ---> SSL_ACCEPT ---> (*) CONNECTED ---> JOINED --->
+ * ---> SSL_SHUTDOWN ---> DEFUNCT
+ *
+ * Connect:
+ *
+ * STATE_NONE ---> (*) WAIT_CONNECT ---> SSL_CONNECT ---> CONNECTED --->
+ * ---> JOINED ---> SSL_SHUTDOWN ---> DEFUNCT
+ *
+ * In states where file descriptors has been published, and where
+ * something goes wrong, the state of the connection is set to
+ * DEFUNCT. A connection in such a state can only be closed by a CLOSE
+ * message from Erlang (a reception of such a message is registered in
+ * cp->closed). The possible states are: WAIT_CONNECT, SSL_CONNECT,
+ * CONNECTED, JOINED, and SSL_SHUTDOWN.
+ *
+ * A connection in state SSL_ACCEPT can be closed and removed without
+ * synchronization.
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef __WIN32__
+#include "esock_winsock.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#ifdef __WIN32__
+#include <process.h>
+#else
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff /* Should be in <netinet/in.h>. */
+#endif
+
+#include "esock.h"
+#include "debuglog.h"
+#include "esock_utils.h"
+#include "esock_ssl.h"
+#include "esock_osio.h"
+#include "esock_posix_str.h"
+#include "esock_poll.h"
+
+#define MAJOR_VERSION 2
+#define MINOR_VERSION 0
+#define MAXREPLYBUF 256
+#define RWBUFLEN (32*1024)
+#define IS_CLIENT 0
+#define IS_SERVER 1
+#define SELECT_TIMEOUT 2 /* seconds */
+
+#define psx_errstr() esock_posix_str(sock_errno())
+#define ssl_errstr() esock_ssl_errstr
+
+#define PROXY_TO_SSL_VALID(cp) (!(cp)->bp && \
+ ((cp)->wq.len > 0 || !(cp)->proxy->eof))
+
+#define SSL_TO_PROXY_VALID(cp) (!(cp)->proxy->bp && \
+ ((cp)->proxy->wq.len > 0 || !(cp)->eof))
+
+#define JOINED_STATE_INVALID(cp) (!(PROXY_TO_SSL_VALID(cp)) && \
+ !(SSL_TO_PROXY_VALID(cp)))
+static int loop(void);
+static int set_poll_conns(Connection *cp, EsockPoll *ep, int verbose);
+static Connection *next_polled_conn(Connection *cp, Connection **cpnext,
+ EsockPoll *ep, int set_wq_fds);
+
+static void leave_joined_state(Connection *cp);
+static void do_shutdown(Connection *cp);
+static void close_and_remove_connection(Connection *cp);
+static int reply(int cmd, char *fmt, ...);
+static int input(char *fmt, ...);
+static int put_pars(unsigned char *buf, char *fmt, va_list args);
+static int get_pars(unsigned char *buf, char *fmt, va_list args);
+static FD do_connect(char *lipstring, int lport, char *fipstring, int fport);
+static FD do_listen(char *ipstring, int lport, int backlog, int *aport);
+static FD do_accept(FD listensock, struct sockaddr *saddr, int *len);
+static void print_connections(void);
+static void dump_connections(void);
+static int check_num_sock_fds(FD fd);
+static void safe_close(FD fd);
+static Connection *new_connection(int state, FD fd);
+static Connection *get_connection(FD fd);
+static void remove_connection(Connection *conn);
+static Proxy *get_proxy_by_peerport(int port);
+static Proxy *new_proxy(FD fd);
+static void remove_proxy(Proxy *proxy);
+static void ensure_write_queue(WriteQueue *wq, int size);
+static void clean_up(void);
+
+static Connection *connections = NULL;
+static int num_sock_fds; /* On UNIX all file descriptors */
+static Proxy *proxies = NULL;
+static int proxy_listensock = INVALID_FD;
+static int proxy_listenport = 0;
+static int proxy_backlog = 128;
+static int proxysock_last_err = 0;
+static int proxysock_err_cnt = 0;
+static char rwbuf[RWBUFLEN];
+static unsigned char *ebuf = NULL; /* Set by read_ctrl() */
+
+static char *connstr[] = {
+ "STATE_NONE",
+ "ACTIVE_LISTENING",
+ "PASSIVE_LISTENING",
+ "CONNECTED",
+ "WAIT_CONNECT",
+ "SSL_CONNECT",
+ "SSL_ACCEPT",
+ "TRANSPORT_ACCEPT",
+ "JOINED",
+ "SSL_SHUTDOWN",
+ "DEFUNCT"
+};
+
+static char *originstr[] = {
+ "listen",
+ "accept",
+ "connect"
+};
+
+int main(int argc, char **argv)
+{
+ char *logfile = NULL;
+ int i;
+ esock_version *vsn;
+ char *ciphers;
+#ifdef __WIN32__
+ int pid;
+ WORD version;
+ WSADATA wsa_data;
+
+ set_binary_mode();
+ setvbuf(stderr, NULL, _IONBF, 0);
+ /* Two sockets for the stdin socket pipe (local thread). */
+ num_sock_fds = 2;
+#else
+ pid_t pid;
+ num_sock_fds = 3; /* 0, 1, 2 */
+#endif
+
+ pid = getpid();
+ i = 1;
+ while (i < argc) {
+ if (strcmp(argv[i], "-d") == 0) {
+ debug = 1;
+ i++;
+ } else if (strcmp(argv[i], "-dm") == 0) {
+ debugmsg = 1;
+ i++;
+ } else if (strcmp(argv[i], "-pp") == 0) {
+ i++;
+ proxy_listenport = atoi(argv[i]);
+ i++;
+ } else if (strcmp(argv[i], "-pb") == 0) {
+ i++;
+ proxy_backlog = atoi(argv[i]);
+ i++;
+ } else if (strcmp(argv[i], "-pv") == 0) {
+ i++;
+ protocol_version = atoi(argv[i]);
+ i++;
+ } else if (strcmp(argv[i], "-dd") == 0) {
+ i++;
+ logfile = esock_malloc(strlen(argv[i]) + 64);
+ sprintf(logfile, "%s/ssl_esock.%d.log", argv[i], (int)pid);
+ i++;
+ } else if (strcmp(argv[i], "-ersa") == 0) {
+ ephemeral_rsa = 1;
+ i++;
+ } else if (strcmp(argv[i], "-edh") == 0) {
+ ephemeral_dh = 1;
+ i++;
+ }
+ }
+ if (debug || debugmsg) {
+ DEBUGF(("Starting ssl_esock\n"));
+ if (logfile) {
+ open_ssllog(logfile);
+#ifndef __WIN32__
+ num_sock_fds++;
+#endif
+ }
+ atexit(close_ssllog);
+ DEBUGF(("pid = %d\n", getpid()));
+ }
+ if (esock_ssl_init() < 0) {
+ fprintf(stderr, "esock: Could not do esock_ssl_init\n");
+ exit(EXIT_FAILURE);
+ }
+
+ atexit(esock_ssl_finish);
+
+#ifdef __WIN32__
+ /* Start Windows' sockets */
+ version = MAKEWORD(MAJOR_VERSION, MINOR_VERSION);
+ if (WSAStartup(version, &wsa_data) != 0) {
+ fprintf(stderr, "esock: Could not start up Windows' sockets\n");
+ exit(EXIT_FAILURE);
+ }
+ atexit((void (*)(void))WSACleanup);
+ if (LOBYTE(wsa_data.wVersion) < MAJOR_VERSION ||
+ (LOBYTE(wsa_data.wVersion) == MAJOR_VERSION &&
+ HIBYTE(wsa_data.wVersion) < MINOR_VERSION)) {
+ fprintf(stderr, "esock: Windows socket version error. "
+ "Requested version:"
+ "%d.%d, version found: %d.%d\n", MAJOR_VERSION,
+ MINOR_VERSION, LOBYTE(wsa_data.wVersion),
+ HIBYTE(wsa_data.wVersion));
+ exit(EXIT_FAILURE);
+ }
+ DEBUGF(("Using Windows socket version: %d.%d\n",
+ LOBYTE(wsa_data.wVersion), HIBYTE(wsa_data.wVersion)));
+ DEBUGF(("Maximum number of sockets available: %d\n",
+ wsa_data.iMaxSockets));
+
+ if (esock_osio_init() < 0) {
+ fprintf(stderr, "esock: Could not init osio\n");
+ exit(EXIT_FAILURE);
+ }
+ atexit(esock_osio_finish);
+#endif
+
+ /* Create the local proxy listen socket and set it to non-blocking */
+ proxy_listensock = do_listen("127.0.0.1", proxy_listenport,
+ proxy_backlog, &proxy_listenport);
+ if (proxy_listensock == INVALID_FD) {
+ fprintf(stderr, "esock: Cannot create local listen socket\n");
+ exit(EXIT_FAILURE);
+ }
+ SET_NONBLOCKING(proxy_listensock);
+ DEBUGF(("Local proxy listen socket: fd = %d, port = %d\n",
+ proxy_listensock, proxy_listenport));
+
+ vsn = esock_ssl_version();
+ ciphers = esock_ssl_ciphers();
+
+ /* Report: port number of the local proxy listen socket, the native
+ * os pid, the compile and lib versions of the ssl library, and
+ * the list of available ciphers. */
+ reply(ESOCK_PROXY_PORT_REP, "24sss", proxy_listenport, (int)pid,
+ vsn->compile_version, vsn->lib_version, ciphers);
+
+ atexit(clean_up);
+
+ loop();
+
+ if (logfile)
+ esock_free(logfile);
+ exit(EXIT_SUCCESS);
+}
+
+
+/*
+ * Local functions
+ *
+ */
+
+static int loop(void)
+{
+ EsockPoll pollfd;
+ FD fd, msgsock, listensock, connectsock, proxysock;
+ int cc, wc, fport, lport, pport, length, backlog, intref, op;
+ int value;
+ char *lipstring, *fipstring;
+ char *flags;
+ char *protocol_vsn, *cipher;
+ unsigned char *cert, *bin;
+ int certlen, binlen;
+ struct sockaddr_in iserv_addr;
+ int sret = 1;
+ Connection *cp, *cpnext, *newcp;
+ Proxy *pp;
+ time_t last_time = 0, now = 0;
+ int set_wq_fds;
+
+ esock_poll_init(&pollfd);
+
+ while(1) {
+ esock_poll_zero(&pollfd);
+ esock_poll_fd_set_read(&pollfd, proxy_listensock);
+ esock_poll_fd_set_read(&pollfd, local_read_fd);
+
+ set_wq_fds = 0;
+
+ if (sret) /* sret == 1 the first time. */
+ DEBUGF(("==========LOOP=============\n"));
+
+ cc = set_poll_conns(connections, &pollfd, sret) + 1;
+
+ if (sret) {
+ print_connections();
+ DEBUGF(("Before poll/select: %d descriptor%s (total %d)\n",
+ cc, (cc == 1) ? "" : "s", num_sock_fds));
+ }
+
+ sret = esock_poll(&pollfd, SELECT_TIMEOUT);
+ if (sret < 0) {
+ DEBUGF(("select/poll error: %s\n", psx_errstr()));
+ continue;
+ }
+
+ time(&now);
+ if (now >= last_time + SELECT_TIMEOUT) {
+ set_wq_fds = 1;
+ last_time = now;
+ }
+ /*
+ * First accept as many connections as possible on the
+ * proxy listen socket. We record the peer port, which
+ * is later used as a reference for joining a proxy
+ * connection with a network connection.
+ */
+
+ if (esock_poll_fd_isset_read(&pollfd, proxy_listensock)) {
+ while (1) {
+ length = sizeof(iserv_addr);
+ proxysock = do_accept(proxy_listensock,
+ (struct sockaddr *)&iserv_addr,
+ (int*)&length);
+ if(proxysock == INVALID_FD) {
+ if (sock_errno() != ERRNO_BLOCK) {
+ /* We can here for example get the error
+ * EMFILE, i.e. no more file descriptors
+ * available, but we do not have any specific
+ * connection to report the error to. We
+ * increment the error counter and saves the
+ * last err.
+ */
+ proxysock_err_cnt++;
+ proxysock_last_err = sock_errno();
+ DEBUGF(("accept error (proxy_listensock): %s\n",
+ psx_errstr()));
+ }
+ break;
+ } else {
+ /* Get peer port number */
+/* length = sizeof(iserv_addr); */
+/* if (getpeername(proxysock, (struct sockaddr *)&iserv_addr, */
+/* &length) < 0) { */
+/* DEBUGF(("Can't get peername of proxy socket")); */
+/* safe_close(proxysock); */
+/* } else { */
+ /* Add to pending proxy connections */
+ SET_NONBLOCKING(proxysock);
+ pp = new_proxy(proxysock);
+ pp->peer_port = ntohs(iserv_addr.sin_port);
+ DEBUGF(("-----------------------------------\n"));
+ DEBUGF(("[PROXY_LISTEN_SOCK] conn accepted: "
+ "proxyfd = %d, "
+ "peer port = %d\n", proxysock, pp->peer_port));
+/* } */
+ }
+ }
+ }
+
+ /*
+ * Read control messages from Erlang
+ */
+ if (esock_poll_fd_isset_read(&pollfd, local_read_fd)) {
+ cc = read_ctrl(&ebuf);
+ if ( cc < 0 ) {
+ DEBUGF(("Read loop -1 or 0\n"));
+ return -1;
+ } else if (cc == 0) { /* not eof */
+ DEBUGF(("GOT empty string \n"));
+
+ } else {
+
+ switch((int)*ebuf) {
+
+ case ESOCK_SET_SEED_CMD:
+ /*
+ * ebuf = {cmd(1), binary(N) }
+ */
+ input("b", &binlen, &bin);
+ DEBUGF(("[SET_SEED_CMD]\n"));
+ esock_ssl_seed(bin, binlen);
+ /* no reply */
+ break;
+
+ case ESOCK_GETPEERNAME_CMD:
+ /*
+ * ebuf = {cmd(1), fd(4)}
+ */
+ input("4", &fd);
+ DEBUGF(("[GETPEERNAME_CMD] fd = %d\n", fd));
+ cp = get_connection(fd);
+ length = sizeof(iserv_addr);
+ if (!cp) {
+ sock_set_errno(ERRNO_NOTSOCK);
+ reply(ESOCK_GETPEERNAME_ERR, "4s", fd, psx_errstr());
+ } else if (getpeername(fd,
+ (struct sockaddr *) &iserv_addr,
+ &length) < 0) {
+ reply(ESOCK_GETPEERNAME_ERR, "4s", fd, psx_errstr());
+ } else {
+ /*
+ * reply = {cmd(1), fd(4), port(2),
+ * ipstring(N), 0(1)}
+ */
+ reply(ESOCK_GETPEERNAME_REP, "42s", fd,
+ ntohs(iserv_addr.sin_port),
+ inet_ntoa(iserv_addr.sin_addr));
+ }
+ break;
+
+ case ESOCK_GETSOCKNAME_CMD:
+ /*
+ * ebuf = {cmd(1), fd(4)}
+ */
+ input("4", &fd);
+ DEBUGF(("[GETSOCKNAME_CMD] fd = %d\n", fd));
+ cp = get_connection(fd);
+ length = sizeof(iserv_addr);
+ if (!cp) {
+ sock_set_errno(ERRNO_NOTSOCK);
+ reply(ESOCK_GETSOCKNAME_ERR, "4s", fd, psx_errstr());
+ } else if (getsockname(fd,
+ (struct sockaddr *)&iserv_addr,
+ &length) < 0) {
+ reply(ESOCK_GETSOCKNAME_ERR, "4s", fd, psx_errstr());
+ } else {
+ /*
+ * reply = {cmd(1), fd(4), port(2),
+ * ipstring(N), 0(1)}
+ */
+ reply(ESOCK_GETSOCKNAME_REP, "42s", fd,
+ ntohs(iserv_addr.sin_port),
+ inet_ntoa(iserv_addr.sin_addr));
+ }
+ break;
+
+ case ESOCK_GETCONNINFO_CMD:
+ /*
+ * ebuf = {cmd(1), fd(4)}
+ */
+ input("4", &fd);
+ DEBUGF(("[GETCONNINFO_CMD] fd = %d\n", fd));
+ cp = get_connection(fd);
+ if (!cp) {
+ sock_set_errno(ERRNO_NOTSOCK);
+ reply(ESOCK_GETCONNINFO_ERR, "4s", fd, psx_errstr());
+ } else {
+ if (esock_ssl_getprotocol_version(cp,
+ &protocol_vsn) < 0)
+ reply(ESOCK_GETCONNINFO_ERR, "4s", fd, psx_errstr());
+ else if (esock_ssl_getcipher(cp, &cipher) < 0)
+ reply(ESOCK_GETCONNINFO_ERR, "4s", fd, psx_errstr());
+ else
+ /*
+ * reply = {cmd(1), fd(4), protocol(N), 0(1),
+ * cipher(N), 0(1)}
+ */
+ reply(ESOCK_GETCONNINFO_REP, "4ss", fd,
+ protocol_vsn, cipher);
+ }
+ break;
+
+ case ESOCK_GETPEERCERT_CMD:
+ /*
+ * ebuf = {cmd(1), fd(4)}
+ */
+ input("4", &fd);
+ DEBUGF(("[GETPEERCERT_CMD] fd = %d\n", fd));
+ cp = get_connection(fd);
+ if (!cp) {
+ sock_set_errno(ERRNO_NOTSOCK);
+ reply(ESOCK_GETPEERCERT_ERR, "4s", fd, psx_errstr());
+ } else {
+ if ((certlen = esock_ssl_getpeercert(cp, &cert)) < 0)
+ reply(ESOCK_GETPEERCERT_ERR, "4s", fd, psx_errstr());
+ else {
+ /*
+ * reply = {cmd(1), fd(4), certlen(4), cert(N)}
+ */
+ reply(ESOCK_GETPEERCERT_REP, "4b", fd,
+ certlen, cert);
+ esock_free(cert);
+ }
+ }
+ break;
+
+ case ESOCK_CONNECT_CMD:
+ /*
+ * ebuf = {cmd(1), intref(4),
+ * lport(2), lipstring(N), 0(1), -- local
+ * fport(2), fipstring(N), 0(1), -- foreign
+ * flags(N), 0(1)}
+ */
+ input("42s2ss", &intref, &lport, &lipstring,
+ &fport, &fipstring, &flags);
+ DEBUGF(("[CONNECT_CMD] intref = %d, "
+ "lipstring = %s lport = %d, "
+ "fipstring = %s fport = %d, "
+ "flags = %s\n", intref, lipstring, lport,
+ fipstring, fport, flags));
+ connectsock = do_connect(lipstring, lport,
+ fipstring, fport);
+ if(connectsock == INVALID_FD) {
+ reply(ESOCK_CONNECT_SYNC_ERR, "4s", intref, psx_errstr());
+ break;
+ }
+ DEBUGF((" fd = %d\n", connectsock));
+ cp = new_connection(ESOCK_WAIT_CONNECT, connectsock);
+ cp->origin = ORIG_CONNECT;
+ length = strlen(flags);
+ cp->flags = esock_malloc(length + 1);
+ strcpy(cp->flags, flags);
+ DEBUGF(("-> WAIT_CONNECT fd = %d\n", connectsock));
+ /* Publish connectsock */
+ reply(ESOCK_CONNECT_WAIT_REP, "44", intref, connectsock);
+ break;
+
+ case ESOCK_TERMINATE_CMD:
+ /*
+ * ebuf = {cmd(1)}
+ */
+ exit(EXIT_SUCCESS);
+ break;
+
+ case ESOCK_CLOSE_CMD:
+ /*
+ * ebuf = {cmd(1), fd(4)}
+ */
+ input("4", &fd);
+ if ((cp = get_connection(fd))) {
+ DEBUGF(("%s[CLOSE_CMD]: fd = %d\n",
+ connstr[cp->state], fd));
+ if (cp->proxy)
+ cp->proxy->bp = 1;
+ switch (cp->state) {
+ case ESOCK_JOINED:
+ cp->close = 1;
+ if (JOINED_STATE_INVALID(cp))
+ leave_joined_state(cp);
+ break;
+ case ESOCK_SSL_SHUTDOWN:
+ cp->close = 1;
+ DEBUGF((" close flag set\n"));
+ break;
+ default:
+ DEBUGF(("-> (removal)\n"));
+ close_and_remove_connection(cp);
+ }
+ } else
+ DEBUGF(("[CLOSE_CMD]: ERROR: fd = %d not found\n", fd));
+ break;
+
+ case ESOCK_SET_SOCKOPT_CMD:
+ /*
+ * ebuf = {cmd(1), fd(4), op(1), on(1)}
+ */
+ input("411", &fd, &op, &value);
+ switch(op) {
+ case ESOCK_SET_TCP_NODELAY:
+ if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
+ (void *)&value, sizeof(value)) < 0) {
+ DEBUGF(("Error: setsockopt TCP_NODELAY\n"));
+ reply(ESOCK_IOCTL_ERR, "4s", fd, psx_errstr());
+ } else {
+ reply(ESOCK_IOCTL_OK, "4", fd);
+ }
+ break;
+ default:
+ DEBUGF(("Error: set_sock_opt - Not implemented\n"));
+ sock_set_errno(ERRNO_OPNOTSUPP);
+ reply(ESOCK_IOCTL_ERR, "4", fd, psx_errstr());
+ break;
+ }
+ break;
+
+ case ESOCK_LISTEN_CMD:
+ /*
+ * ebuf = {cmd(1), intref(4), lport(2), ipstring(N), 0(1),
+ * backlog(2), flags(N), 0(1)}
+ */
+ input("42s2s", &intref, &lport, &lipstring, &backlog,
+ &flags);
+ DEBUGF(("[LISTEN_CMD] intref = %d, port = %d, "
+ "ipstring = %s, backlog = %d, flags = %s\n",
+ intref, lport, lipstring, backlog, flags));
+
+ listensock = do_listen(lipstring, lport, backlog, &lport);
+ if(listensock == INVALID_FD) {
+ reply(ESOCK_LISTEN_SYNC_ERR, "4s", intref, psx_errstr());
+ break;
+ }
+ cp = new_connection(ESOCK_PASSIVE_LISTENING, listensock);
+ /* Flags may be an empty string */
+ length = strlen(flags);
+ cp->flags = esock_malloc(length + 1);
+ strcpy(cp->flags, flags);
+
+ cp->origin = ORIG_LISTEN;
+ if (esock_ssl_listen_init(cp) < 0) {
+ DEBUGF(("esock_ssl_listen_init() failed.\n"));
+ reply(ESOCK_LISTEN_SYNC_ERR, "4s", intref,
+ ssl_errstr());
+ close_and_remove_connection(cp);
+ break;
+ }
+ DEBUGF(("-> PASSIVE_LISTENING (fd = %d)\n", listensock));
+ /* Publish listensock */
+ reply(ESOCK_LISTEN_REP, "442", intref, listensock,
+ ntohs(iserv_addr.sin_port));
+ break;
+
+ case ESOCK_TRANSPORT_ACCEPT_CMD:
+ /*
+ * ebuf = { op(1), fd(4), flags(N), 0(1)}
+ */
+ input("4s", &fd, &flags);
+ DEBUGF(("[TRANSPORT_ACCEPT_CMD] listenfd = %d, flags = %s\n", fd,
+ flags));
+ cp = get_connection(fd);
+ if (cp) {
+ /* We store the flags in the listen socket's
+ * connection, and overwrite previous flags.
+ */
+ if ((length = strlen(flags)) > 0) {
+ if (cp->flags)
+ cp->flags = esock_realloc(cp->flags,
+ length + 1);
+ else
+ cp->flags = esock_malloc(length + 1);
+ strcpy(cp->flags, flags);
+ }
+ if (cp->flags && cp->flags[0] != '\0') {
+ cp->acceptors++;
+ cp->state = ESOCK_ACTIVE_LISTENING;
+ DEBUGF(("-> ACTIVE_LISTENING\n"));
+ break;
+ }
+ DEBUGF(("ERROR: flags empty\n"));
+ }
+ reply(ESOCK_TRANSPORT_ACCEPT_ERR, "4s", fd, "ebadf");
+ break;
+
+ case ESOCK_SSL_ACCEPT_CMD:
+ input("4s", &fd, &flags);
+ DEBUGF(("[SSL_ACCEPT_CMD] fd = %d, flags = %s\n", fd, flags));
+ cp = get_connection(fd);
+ if (cp)
+ cp->state = ESOCK_SSL_ACCEPT;
+ //reply(ESOCK_SSL_ACCEPT_REP, "4", fd);
+ break;
+
+ case ESOCK_NOACCEPT_CMD:
+ /*
+ * ebuf = {cmd(1), fd(4)}
+ */
+ input("4", &fd);
+ DEBUGF(("[NOACCEPT_CMD] listenfd = %d\n", fd));
+ cp = get_connection(fd);
+ if (cp && (--cp->acceptors <= 0)) {
+ cp->acceptors = 0;
+ cp->state = ESOCK_PASSIVE_LISTENING;
+ esock_poll_clear_event(&pollfd, fd);
+ DEBUGF(("-> PASSIVE_LISTENING\n"));
+ }
+ break;
+
+ case ESOCK_PROXY_JOIN_CMD:
+ /*
+ * ebuf = {cmd(1), fd(4), portnum(2)}
+ *
+ * fd - file descriptor of a connection in state
+ * CONNECTED
+ * portnum - port number of the Erlang proxy peer
+ */
+ input("42", &fd, &pport);
+ cp = get_connection(fd);
+ pp = get_proxy_by_peerport(pport);
+ if (cp && cp->state == ESOCK_CONNECTED && pp) {
+ DEBUGF(("CONNECTED[PROXY_JOIN_CMD] fd = %d "
+ "portnum = %d\n", fd, pport));
+ cp->proxy = pp;
+ pp->conn = cp;
+ reply(ESOCK_PROXY_JOIN_REP, "4", fd);
+ cp->state = ESOCK_JOINED;
+ DEBUGF(("-> JOINED\n"));
+ break;
+ }
+ if (!cp) {
+ DEBUGF(("[PROXY_JOIN_CMD] ERROR: No connection "
+ "having fd = %d\n", fd));
+ reply(ESOCK_PROXY_JOIN_ERR, "4s", fd, "ebadsocket");
+ } else if (cp->state != ESOCK_CONNECTED) {
+ DEBUGF(("%s[PROXY_JOIN_CMD] ERROR: Bad state: "
+ "fd = %d\n", connstr[cp->state], cp->fd));
+ reply(ESOCK_PROXY_JOIN_ERR, "4s", fd, "ebadstate");
+ } else {
+ DEBUGF(("ERROR: No proxy: fd = %d, pport = %d\n",
+ fd, pport));
+ if (proxysock_err_cnt > 0) {
+ proxysock_err_cnt--;
+ reply(ESOCK_PROXY_JOIN_ERR, "4s", fd,
+ esock_posix_str(proxysock_last_err));
+ } else {
+ reply(ESOCK_PROXY_JOIN_ERR, "4s", fd,
+ "enoproxysocket");
+ }
+ cp->state = ESOCK_DEFUNCT;
+ }
+ break;
+
+ case ESOCK_DUMP_STATE_CMD:
+ dump_connections();
+ break;
+
+ case ESOCK_SET_DEBUG_CMD:
+ /*
+ * ebuf = {cmd(1), debug(1)}
+ */
+ input("1", &debug);
+ break;
+
+ case ESOCK_SET_DEBUGMSG_CMD:
+ /*
+ * ebuf = {cmd(1), debugmsg(1)}
+ */
+ input("1", &debugmsg);
+ break;
+
+ default:
+ fprintf(stderr, "esock: default value in loop %c\n",
+ *ebuf);
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+ }
+
+ /* Go through all connections that have their file descriptors
+ set. */
+
+ /* Note: We may remove the current connection (cp). Thus we
+ * must be careful not to read cp->next after cp has been
+ * removed. */
+ for (cp = next_polled_conn(connections, &cpnext, &pollfd, set_wq_fds);
+ cp != NULL;
+ cp = next_polled_conn(cpnext, &cpnext, &pollfd, set_wq_fds)
+ ) {
+
+ switch(cp->state) {
+
+ case ESOCK_PASSIVE_LISTENING:
+ DEBUGF(("-----------------------------------\n"));
+ fprintf(stderr, "esock: Got connect request while PASSIVE\n");
+ exit(EXIT_FAILURE);
+ break;
+
+ case ESOCK_ACTIVE_LISTENING:
+ /* new connect from network */
+ DEBUGF(("-----------------------------------\n"));
+ DEBUGF(("ACTIVE_LISTENING - trying to accept on %d\n",
+ cp->fd));
+ length = sizeof(iserv_addr);
+ msgsock = do_accept(cp->fd, (struct sockaddr*)&iserv_addr,
+ (int*)&length);
+ if(msgsock == INVALID_FD) {
+ DEBUGF(("accept error: %s\n", psx_errstr()));
+ reply(ESOCK_TRANSPORT_ACCEPT_ERR, "4s", cp->fd, psx_errstr());
+ break;
+ }
+ SET_NONBLOCKING(msgsock);
+ if (--cp->acceptors <= 0) {
+ cp->acceptors = 0;
+ cp->state = ESOCK_PASSIVE_LISTENING;
+ DEBUGF(("-> PASSIVE_LISTENING\n"));
+ }
+ DEBUGF(("server accepted connection on fd %d\n", msgsock));
+ newcp = new_connection(ESOCK_TRANSPORT_ACCEPT, msgsock);
+ newcp->origin = ORIG_ACCEPT;
+ reply(ESOCK_TRANSPORT_ACCEPT_REP, "44", cp->fd, msgsock);
+ newcp->listen_fd = cp->fd; /* Needed for ESOCK_ACCEPT_ERR */
+ length = strlen(cp->flags);
+ /* XXX new flags are not needed */
+ newcp->flags = esock_malloc(length + 1);
+ strcpy(newcp->flags, cp->flags); /* XXX Why? */
+ if (esock_ssl_accept_init(newcp, cp->opaque) < 0) {
+ cp->errstr = ssl_errstr();
+ break;
+ }
+ newcp->ssl_want = ESOCK_SSL_WANT_READ;
+ break;
+
+ case ESOCK_SSL_ACCEPT:
+ /* SSL accept handshake. msgsock is *not* published yet. */
+ msgsock = cp->fd;
+ DEBUGF(("-----------------------------------\n"));
+ DEBUGF(("SSL_ACCEPT fd = %d\n", msgsock));
+ if (cp->errstr != NULL) { /* this means we got an error in ssl_accept_init */
+ /* N.B.: The *listen fd* is reported. */
+ reply(ESOCK_SSL_ACCEPT_ERR, "4s", msgsock, cp->errstr);
+ close_and_remove_connection(cp);
+ break;
+ }
+ if (esock_ssl_accept(cp) < 0) {
+ if (sock_errno() != ERRNO_BLOCK) {
+ /* Handshake failed. */
+ reply(ESOCK_SSL_ACCEPT_ERR, "4s", msgsock,
+ ssl_errstr());
+ DEBUGF(("ERROR: handshake: %s\n", ssl_errstr()));
+ close_and_remove_connection(cp);
+ }
+ } else {
+ /* SSL handshake successful: publish */
+ reply(ESOCK_SSL_ACCEPT_REP, "4", msgsock);
+ DEBUGF(("-> CONNECTED\n"));
+ DEBUGF((" Session was %sreused.\n",
+ (esock_ssl_session_reused(cp)) ? "" : "NOT "));
+ cp->state = ESOCK_CONNECTED;
+ }
+ break;
+
+ case ESOCK_CONNECTED:
+ /* Should not happen. We do not read or write until
+ the connection is in state JOINED. */
+ DEBUGF(("-----------------------------------\n"));
+ DEBUGF(("CONNECTED: Error: should not happen. fd = %d\n",
+ cp->fd));
+ break;
+
+ case ESOCK_JOINED:
+ /*
+ * Reading from Proxy, writing to SSL
+ */
+ if (esock_poll_fd_isset_write(&pollfd, cp->fd)) {
+ /* If there is a write queue, write to ssl only */
+ if (cp->wq.len > 0) {
+ /* The write retry semantics of SSL_write in
+ * the OpenSSL package is strange. Partial
+ * writes never occur, only complete writes or
+ * failures. A failure, however, still
+ * consumes all data written, although not all
+ * encrypted data could be written to the
+ * underlying socket. To retry a write we have
+ * to provide the same buf and length as in
+ * the original call, in our case rwbuf and
+ * the original buffer length. Hence the
+ * strange memcpy(). Note that wq.offset will
+ * always be zero when we use OpenSSL.
+ */
+ DEBUGF(("-----------------------------------\n"));
+ DEBUGF(("JOINED: writing to ssl "
+ "fd = %d, from write queue only, wc = %d\n",
+ cp->fd, cp->wq.len - cp->wq.offset));
+ memcpy(rwbuf, cp->wq.buf, cp->wq.len - cp->wq.offset);
+
+ /* esock_ssl_write sets cp->eof, cp->bp when return
+ * value is zero */
+ wc = esock_ssl_write(cp, rwbuf,
+ cp->wq.len - cp->wq.offset);
+ if (wc < 0) {
+ if (sock_errno() != ERRNO_BLOCK) {
+ /* Assume broken SSL pipe */
+ DEBUGF(("broken SSL pipe\n"));
+ cp->bp = 1;
+ shutdown(cp->proxy->fd, SHUTDOWN_READ);
+ cp->proxy->eof = 1;
+ if (JOINED_STATE_INVALID(cp)) {
+ leave_joined_state(cp);
+ break;
+ }
+ }
+ } else if (wc == 0) {
+ /* SSL broken pipe */
+ DEBUGF(("broken SSL pipe\n"));
+ cp->bp = 1;
+ shutdown(cp->proxy->fd, SHUTDOWN_READ);
+ cp->proxy->eof = 1;
+ if (JOINED_STATE_INVALID(cp)) {
+ leave_joined_state(cp);
+ break;
+ }
+ } else {
+ cp->wq.offset += wc;
+ if (cp->wq.offset == cp->wq.len)
+ cp->wq.len = 0;
+ }
+ }
+ } else if (esock_poll_fd_isset_read(&pollfd, cp->proxy->fd)) {
+ /* Read from proxy and write to SSL */
+ DEBUGF(("-----------------------------------\n"));
+ DEBUGF(("JOINED: reading from proxy, "
+ "proxyfd = %d\n", cp->proxy->fd));
+ cc = sock_read(cp->proxy->fd, rwbuf, RWBUFLEN);
+ DEBUGF(("read from proxyfd = %d, cc = %d\n",
+ cp->proxy->fd, cc));
+ if (cc > 0) {
+ /* esock_ssl_write sets cp->eof, cp->bp when return
+ * value is zero */
+ wc = esock_ssl_write(cp, rwbuf, cc);
+ if (wc < 0) {
+ if (sock_errno() != ERRNO_BLOCK) {
+ /* Assume broken pipe */
+ DEBUGF(("broken SSL pipe\n"));
+ cp->bp = 1;
+ shutdown(cp->proxy->fd, SHUTDOWN_READ);
+ cp->proxy->eof = 1;
+ if (JOINED_STATE_INVALID(cp)) {
+ leave_joined_state(cp);
+ break;
+ }
+ } else {
+ /* add to write queue */
+ DEBUGF(("adding all to write queue "
+ "%d bytes\n", cc));
+ ensure_write_queue(&cp->wq, cc);
+ memcpy(cp->wq.buf, rwbuf, cc);
+ cp->wq.len = cc;
+ cp->wq.offset = 0;
+ }
+ } else if (wc == 0) {
+ /* Broken SSL pipe */
+ DEBUGF(("broken SSL pipe\n"));
+ cp->bp = 1;
+ shutdown(cp->proxy->fd, SHUTDOWN_READ);
+ cp->proxy->eof = 1;
+ if (JOINED_STATE_INVALID(cp)) {
+ leave_joined_state(cp);
+ break;
+ }
+ } else if (wc < cc) {
+ /* add remainder to write queue */
+ DEBUGF(("adding remainder to write queue "
+ "%d bytes\n", cc - wc));
+ ensure_write_queue(&cp->wq, cc - wc);
+ memcpy(cp->wq.buf, rwbuf + wc, cc - wc);
+ cp->wq.len = cc - wc;
+ cp->wq.offset = 0;
+ }
+ } else {
+ /* EOF proxy or error */
+ DEBUGF(("proxy eof or error %d\n", errno));
+ cp->proxy->eof = 1;
+ if (cp->wq.len == 0) {
+ esock_ssl_shutdown(cp);
+ cp->bp = 1;
+ }
+ if (JOINED_STATE_INVALID(cp)) {
+ leave_joined_state(cp);
+ break;
+ }
+ }
+ }
+ /*
+ * Reading from SSL, writing to proxy
+ */
+ if (esock_poll_fd_isset_write(&pollfd, cp->proxy->fd)) {
+ /* If there is a write queue, write to proxy only */
+ if (cp->proxy->wq.len > 0) {
+ DEBUGF(("-----------------------------------\n"));
+ DEBUGF(("JOINED: writing to proxyfd = %d, "
+ "from write queue only, wc = %d\n",
+ cp->proxy->fd, cp->proxy->wq.len -
+ cp->proxy->wq.offset));
+ wc = sock_write(cp->proxy->fd, cp->proxy->wq.buf +
+ cp->proxy->wq.offset,
+ cp->proxy->wq.len -
+ cp->proxy->wq.offset);
+ if (wc < 0) {
+ if (sock_errno() != ERRNO_BLOCK) {
+ /* Assume broken pipe */
+ DEBUGF(("broken proxy pipe\n"));
+ cp->proxy->bp = 1;
+ /* There is no SSL shutdown for read */
+ cp->eof = 1;
+ if (JOINED_STATE_INVALID(cp)) {
+ leave_joined_state(cp);
+ break;
+ }
+ }
+ } else {
+ cp->proxy->wq.offset += wc;
+ if (cp->proxy->wq.offset == cp->proxy->wq.len)
+ cp->proxy->wq.len = 0;
+ }
+ }
+ } else if (esock_poll_fd_isset_read(&pollfd, cp->fd)) {
+ /* Read from SSL and write to proxy */
+ DEBUGF(("-----------------------------------\n"));
+ DEBUGF(("JOINED: read from ssl fd = %d\n",
+ cp->fd));
+ cc = esock_ssl_read(cp, rwbuf, RWBUFLEN);
+ DEBUGF(("read from fd = %d, cc = %d\n", cp->fd, cc));
+ if (cc > 0) {
+ wc = sock_write(cp->proxy->fd, rwbuf, cc);
+ if (wc < 0) {
+ if (sock_errno() != ERRNO_BLOCK) {
+ DEBUGF(("broken proxy pipe\n"));
+ /* Assume broken pipe */
+ cp->proxy->bp = 1;
+ /* There is no SSL shutdown for read */
+ cp->eof = 1;
+ if (JOINED_STATE_INVALID(cp)) {
+ leave_joined_state(cp);
+ break;
+ }
+ } else {
+ /* add all to write queue */
+ DEBUGF(("adding to write queue %d bytes\n",
+ cc));
+ ensure_write_queue(&cp->proxy->wq, cc);
+ memcpy(cp->proxy->wq.buf, rwbuf, cc);
+ cp->proxy->wq.len = cc;
+ cp->proxy->wq.offset = 0;
+ }
+ } else if (wc < cc) {
+ /* add to write queue */
+ DEBUGF(("adding to write queue %d bytes\n",
+ cc - wc));
+ ensure_write_queue(&cp->proxy->wq, cc - wc);
+ memcpy(cp->proxy->wq.buf, rwbuf + wc, cc - wc);
+ cp->proxy->wq.len = cc - wc;
+ cp->proxy->wq.offset = 0;
+ }
+ } else if (cc == 0) {
+ /* SSL eof */
+ DEBUGF(("SSL eof\n"));
+ cp->eof = 1;
+ if (cp->proxy->wq.len == 0) {
+ shutdown(cp->proxy->fd, SHUTDOWN_WRITE);
+ cp->proxy->bp = 1;
+ }
+ if (JOINED_STATE_INVALID(cp)) {
+ leave_joined_state(cp);
+ break;
+ }
+ } else {
+ /* This may very well happen when reading from SSL. */
+ DEBUGF(("NOTE: readmask set, cc < 0, fd = %d, "
+ "is ok\n", cp->fd));
+ }
+ }
+ break;
+
+ case ESOCK_SSL_SHUTDOWN:
+ DEBUGF(("-----------------------------------\n"));
+ DEBUGF(("SSL_SHUTDOWN: fd = %d\n", cp->fd));
+ do_shutdown(cp);
+ break;
+
+ case ESOCK_DEFUNCT:
+ DEBUGF(("-----------------------------------\n"));
+ DEBUGF(("DEFUNCT: ERROR: should not happen. fd = %d\n",
+ cp->fd));
+ break;
+
+ case ESOCK_WAIT_CONNECT:
+ /* New connection shows up */
+ connectsock = cp->fd;/* Is published */
+ DEBUGF(("-----------------------------------\n"));
+ DEBUGF(("WAIT_CONNECT fd = %d\n", connectsock));
+
+ /* If the connection did succeed it's possible to
+ * fetch the peer name (UNIX); or failure shows in
+ * exceptmask (WIN32). Sorry for the mess below, but
+ * we have to have balanced paren's in #ifdefs in
+ * order not to confuse Emacs' indentation. */
+ length = sizeof(iserv_addr);
+ if (
+#ifdef __WIN32__
+ esock_poll_fd_isset_exception(&pollfd, connectsock)
+#else
+ getpeername(connectsock, (struct sockaddr *)&iserv_addr,
+ &length) < 0
+#endif
+ ) {
+ sock_set_errno(ERRNO_CONNREFUSED);
+ DEBUGF(("connect error: %s\n", psx_errstr()));
+ reply(ESOCK_CONNECT_ERR, "4s", connectsock, psx_errstr());
+ cp->state = ESOCK_DEFUNCT;
+ break;
+ }
+ if (esock_ssl_connect_init(cp) < 0) {
+ DEBUGF(("esock_ssl_connect_init() failed\n"));
+ reply(ESOCK_CONNECT_ERR, "4s", connectsock, ssl_errstr());
+ cp->state = ESOCK_DEFUNCT;
+ break;
+ }
+ DEBUGF(("-> SSL_CONNECT\n"));
+ cp->state = ESOCK_SSL_CONNECT;
+ cp->ssl_want = ESOCK_SSL_WANT_WRITE;
+ break;
+
+ case ESOCK_SSL_CONNECT:
+ /* SSL connect handshake. connectsock is published. */
+ connectsock = cp->fd;
+ DEBUGF(("-----------------------------------\n"));
+ DEBUGF(("SSL_CONNECT fd = %d\n", connectsock));
+ if (esock_ssl_connect(cp) < 0) {
+ if (sock_errno() != ERRNO_BLOCK) {
+ /* Handshake failed */
+ DEBUGF(("ERROR: handshake: %s\n", ssl_errstr()));
+ reply(ESOCK_CONNECT_ERR, "4s", connectsock,
+ ssl_errstr());
+ cp->state = ESOCK_DEFUNCT;
+ }
+ } else {
+ /* SSL connect handshake successful */
+ DEBUGF(("-> CONNECTED\n"));
+ reply(ESOCK_CONNECT_REP, "4", connectsock);
+ cp->state = ESOCK_CONNECTED;
+ }
+ break;
+
+ default:
+ DEBUGF(("ERROR: Connection in unknown state.\n"));
+ }
+ }
+ }
+}
+
+static int set_poll_conns(Connection *cp, EsockPoll *ep, int verbose)
+{
+ int i = 0;
+
+ if (verbose)
+ DEBUGF(("MASKS SET FOR FD: "));
+ while (cp) {
+ switch (cp->state) {
+ case ESOCK_ACTIVE_LISTENING:
+ if (verbose)
+ DEBUGF(("%d (read) ", cp->fd));
+ esock_poll_fd_set_read(ep, cp->fd);
+ break;
+ case ESOCK_WAIT_CONNECT:
+ if (verbose)
+ DEBUGF(("%d (write) ", cp->fd));
+ esock_poll_fd_set_write(ep, cp->fd);
+#ifdef __WIN32__
+ esock_poll_fd_set_exception(ep, cp->fd); /* Failure shows in exceptions */
+#endif
+ break;
+ case ESOCK_SSL_CONNECT:
+ case ESOCK_SSL_ACCEPT:
+ if (cp->ssl_want == ESOCK_SSL_WANT_READ) {
+ if (verbose)
+ DEBUGF(("%d (read) ", cp->fd));
+ esock_poll_fd_set_read(ep, cp->fd);
+ } else if (cp->ssl_want == ESOCK_SSL_WANT_WRITE) {
+ if (verbose)
+ DEBUGF(("%d (write) ", cp->fd));
+ esock_poll_fd_set_write(ep, cp->fd);
+ }
+ break;
+ case ESOCK_JOINED:
+ if (!cp->bp) {
+ if (cp->wq.len) {
+ if (verbose)
+ DEBUGF(("%d (write) ", cp->fd));
+ esock_poll_fd_set_write(ep, cp->fd);
+ } else if (!cp->proxy->eof) {
+ if (verbose)
+ DEBUGF(("%d (read) ", cp->proxy->fd));
+ esock_poll_fd_set_read(ep, cp->proxy->fd);
+ }
+ }
+ if (!cp->proxy->bp) {
+ if (cp->proxy->wq.len) {
+ if (verbose)
+ DEBUGF(("%d (write) ", cp->proxy->fd));
+ esock_poll_fd_set_write(ep, cp->proxy->fd);
+ } else if (!cp->eof) {
+ if (verbose)
+ DEBUGF(("%d (read) ", cp->fd));
+ esock_poll_fd_set_read(ep, cp->fd);
+ }
+ }
+ break;
+ case ESOCK_SSL_SHUTDOWN:
+ if (cp->ssl_want == ESOCK_SSL_WANT_READ) {
+ if (verbose)
+ DEBUGF(("%d (read) ", cp->fd));
+ esock_poll_fd_set_read(ep, cp->fd);
+ } else if (cp->ssl_want == ESOCK_SSL_WANT_WRITE) {
+ if (verbose)
+ DEBUGF(("%d (write) ", cp->fd));
+ esock_poll_fd_set_write(ep, cp->fd);
+ }
+ break;
+ default:
+ break;
+ }
+ i++;
+ cp = cp->next;
+ }
+ if (verbose)
+ DEBUGF(("\n"));
+ return i;
+}
+
+
+static Connection *next_polled_conn(Connection *cp, Connection **cpnext,
+ EsockPoll *ep, int set_wq_fds)
+{
+ while(cp) {
+ if (esock_poll_fd_isset_read(ep, cp->fd) ||
+ (cp->proxy && esock_poll_fd_isset_read(ep, cp->proxy->fd)) ||
+ (esock_poll_fd_isset_write(ep, cp->fd)) ||
+ (cp->proxy && esock_poll_fd_isset_write(ep, cp->proxy->fd))
+#ifdef __WIN32__
+ || esock_poll_fd_isset_exception(ep, cp->fd) /* Connect failure in WIN32 */
+#endif
+ || (set_wq_fds && (cp->wq.len ||
+ (cp->proxy && cp->proxy->wq.len)))
+ || cp->errstr != NULL) {
+ *cpnext = cp->next;
+ return cp;
+ }
+ cp = cp->next;
+ }
+ *cpnext = NULL;
+ return NULL;
+}
+
+static void leave_joined_state(Connection *cp)
+{
+ shutdown(cp->proxy->fd, SHUTDOWN_ALL);
+ if (((cp->bp || cp->eof) && cp->clean) ||
+ (!cp->bp && !cp->eof)) {
+ DEBUGF(("-> SSL_SHUTDOWN\n"));
+ cp->state = ESOCK_SSL_SHUTDOWN;
+ cp->ssl_want = ESOCK_SSL_WANT_WRITE;
+ do_shutdown(cp);
+ } else if (cp->close) {
+ DEBUGF(("-> (removal)\n"));
+ close_and_remove_connection(cp);
+ } else {
+ DEBUGF(("-> DEFUNCT\n"));
+ cp->state = ESOCK_DEFUNCT;
+ }
+}
+
+/* We are always in state SHUTDOWN here */
+static void do_shutdown(Connection *cp)
+{
+ int ret;
+
+ ret = esock_ssl_shutdown(cp);
+ if (ret < 0) {
+ if (sock_errno() == ERRNO_BLOCK) {
+ return;
+ } else {
+ /* Something is wrong -- close and remove or move to DEFUNCT */
+ DEBUGF(("Error in SSL shutdown\n"));
+ if (cp->close) {
+ DEBUGF(("-> (removal)\n"));
+ close_and_remove_connection(cp);
+ } else {
+ DEBUGF(("-> DEFUNCT\n"));
+ cp->state = ESOCK_DEFUNCT;
+ }
+ }
+ } else if (ret == 0) {
+ /* `close_notify' has been sent. Wait for reception of
+ same. */
+ return;
+ } else if (ret == 1) {
+ /* `close_notify' has been sent, and received. */
+ if (cp->close) {
+ DEBUGF(("-> (removal)\n"));
+ close_and_remove_connection(cp);
+ } else {
+ DEBUGF(("-> DEFUNCT\n"));
+ cp->state = ESOCK_DEFUNCT;
+ }
+ }
+}
+
+static void close_and_remove_connection(Connection *cp)
+{
+ safe_close(cp->fd);
+ remove_connection(cp);
+}
+
+static int reply(int cmd, char *fmt, ...)
+{
+ static unsigned char replybuf[MAXREPLYBUF];
+ unsigned char *buf = replybuf;
+ va_list args;
+ int len;
+
+ va_start(args, fmt);
+ len = put_pars(NULL, fmt, args);
+ va_end(args);
+ len++;
+ if (len > sizeof(replybuf))
+ buf = esock_malloc(len);
+
+ PUT_INT8(cmd, buf);
+ va_start(args, fmt);
+ (void) put_pars(buf + 1, fmt, args);
+ va_end(args);
+ write_ctrl(buf, len);
+ if (buf != replybuf)
+ esock_free(buf);
+ return len;
+}
+
+static int input(char *fmt, ...)
+{
+ va_list args;
+ int len;
+
+ va_start(args, fmt);
+ len = get_pars(ebuf + 1, fmt, args);
+ va_end(args);
+ return len + 1;
+}
+
+static int put_pars(unsigned char *buf, char *fmt, va_list args)
+{
+ char *s, *str, *bin;
+ int val, len, pos = 0;
+
+ s = fmt;
+ while (*s) {
+ switch (*s) {
+ case '1':
+ val = va_arg(args, int);
+ if (buf)
+ PUT_INT8(val, buf + pos);
+ pos++;
+ break;
+ case '2':
+ val = va_arg(args, int);
+ if (buf)
+ PUT_INT16(val, buf + pos);
+ pos += 2;
+ break;
+ case '4':
+ val = va_arg(args, int);
+ if (buf)
+ PUT_INT32(val, buf + pos);
+ pos += 4;
+ break;
+ case 's': /* string */
+ str = va_arg(args, char *);
+ if (buf)
+ strcpy((char *)(buf + pos), str);
+ pos += strlen(str) + 1;
+ break;
+ case 'b': /* binary */
+ len = va_arg(args, int);
+ if (buf)
+ PUT_INT32(len, buf + pos);
+ pos += 4;
+ bin = va_arg(args, char *);
+ if (buf)
+ memcpy(buf + pos, bin, len);
+ pos += len;
+ break;
+ default:
+ fprintf(stderr, "esock: Invalid format character: %c\n", *s);
+ exit(EXIT_FAILURE);
+ break;
+ }
+ s++;
+ }
+ return pos;
+}
+
+
+static int get_pars(unsigned char *buf, char *fmt, va_list args)
+{
+ int *ip;
+ char *s, **strp, **bin;
+ int pos = 0;
+
+ s = fmt;
+ while (*s) {
+ switch (*s) {
+ case '1':
+ ip = va_arg(args, int *);
+ *ip = GET_INT8(buf + pos);
+ pos++;
+ break;
+ case '2':
+ ip = va_arg(args, int *);
+ *ip = GET_INT16(buf + pos);
+ pos += 2;
+ break;
+ case '4':
+ ip = va_arg(args, int *);
+ *ip = GET_INT32(buf + pos);
+ pos += 4;
+ break;
+ case 's':
+ strp = va_arg(args, char **);
+ *strp = (char *)(buf + pos);
+ pos += strlen(*strp) + 1;
+ break;
+ case 'b':
+ ip = va_arg(args, int *);
+ *ip = GET_INT32(buf + pos);
+ pos += 4;
+ bin = va_arg(args, char **);
+ *bin = (char *)(buf + pos);
+ pos += *ip;
+ break;
+ default:
+ fprintf(stderr, "esock: Invalid format character: %c\n", *s);
+ exit(EXIT_FAILURE);
+ break;
+ }
+ s++;
+ }
+ return pos;
+}
+
+static FD do_connect(char *lipstring, int lport, char *fipstring, int fport)
+{
+ struct sockaddr_in sock_addr;
+ long inaddr;
+ FD fd;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_FD) {
+ DEBUGF(("Error calling socket()\n"));
+ return fd;
+ }
+ if (check_num_sock_fds(fd) < 0)
+ return INVALID_FD;
+ DEBUGF((" fd = %d\n", fd));
+
+ /* local */
+ if ((inaddr = inet_addr(lipstring)) == INADDR_NONE) {
+ DEBUGF(("Error in inet_addr(): lipstring = %s\n", lipstring));
+ safe_close(fd);
+ sock_set_errno(ERRNO_ADDRNOTAVAIL);
+ return INVALID_FD;
+ }
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_addr.s_addr = inaddr;
+ sock_addr.sin_port = htons(lport);
+ if(bind(fd, (struct sockaddr*) &sock_addr, sizeof(sock_addr)) < 0) {
+ DEBUGF(("Error in bind()\n"));
+ safe_close(fd);
+ /* XXX Set error code for bind error */
+ return INVALID_FD;
+ }
+
+ /* foreign */
+ if ((inaddr = inet_addr(fipstring)) == INADDR_NONE) {
+ DEBUGF(("Error in inet_addr(): fipstring = %s\n", fipstring));
+ safe_close(fd);
+ sock_set_errno(ERRNO_ADDRNOTAVAIL);
+ return INVALID_FD;
+ }
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_addr.s_addr = inaddr;
+ sock_addr.sin_port = htons(fport);
+
+ SET_NONBLOCKING(fd);
+
+ if(connect(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0) {
+ if (sock_errno() != ERRNO_PROGRESS && /* UNIX */
+ sock_errno() != ERRNO_BLOCK) { /* WIN32 */
+ DEBUGF(("Error in connect()\n"));
+ safe_close(fd);
+ return INVALID_FD;
+ }
+ }
+ return fd;
+}
+
+static FD do_listen(char *ipstring, int lport, int backlog, int *aport)
+{
+ static int one = 1; /* Type must be int, not long */
+ struct sockaddr_in sock_addr;
+ long inaddr;
+ int length;
+ FD fd;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_FD) {
+ DEBUGF(("Error calling socket()\n"));
+ return fd;
+ }
+ if (check_num_sock_fds(fd) < 0)
+ return INVALID_FD;
+ DEBUGF((" fd = %d\n", fd));
+ if ((inaddr = inet_addr(ipstring)) == INADDR_NONE) {
+ DEBUGF(("Error in inet_addr(): ipstring = %s\n", ipstring));
+ safe_close(fd);
+ sock_set_errno(ERRNO_ADDRNOTAVAIL);
+ return INVALID_FD;
+ }
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_addr.s_addr = inaddr;
+ sock_addr.sin_port = htons(lport);
+
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one));
+
+ if(bind(fd, (struct sockaddr*) &sock_addr, sizeof(sock_addr)) < 0) {
+ DEBUGF(("Error in bind()\n"));
+ safe_close(fd);
+ return INVALID_FD;
+ }
+ if (listen(fd, backlog) < 0) {
+ DEBUGF(("Error in listen()\n"));
+ safe_close(fd);
+ return INVALID_FD;
+ }
+ /* find out assigned local port number */
+ length = sizeof(sock_addr);
+ if (getsockname(fd, (struct sockaddr *)&sock_addr, &length) < 0) {
+ DEBUGF(("Error in getsockname()\n"));
+ safe_close(fd);
+ return INVALID_FD;
+ }
+ if (aport)
+ *aport = ntohs(sock_addr.sin_port);
+ return fd;
+}
+
+static FD do_accept(FD listensock, struct sockaddr *saddr, int *len)
+{
+ FD fd;
+
+ if ((fd = accept(listensock, saddr, len)) == INVALID_FD) {
+ DEBUGF(("Error calling accept()\n"));
+ return fd;
+ }
+ if (check_num_sock_fds(fd) < 0)
+ return INVALID_FD;
+ return fd;
+}
+
+static Connection *new_connection(int state, FD fd)
+{
+ Connection *cp;
+
+ if (!(cp = esock_malloc(sizeof(Connection))))
+ return NULL;
+ cp->state = state;
+ cp->acceptors = 0;
+ cp->fd = fd;
+ cp->listen_fd = INVALID_FD;
+ cp->proxy = NULL;
+ cp->opaque = NULL;
+ cp->ssl_want = 0;
+ cp->eof = 0;
+ cp->bp = 0;
+ cp->clean = 0; /* XXX Used? */
+ cp->close = 0;
+ cp->origin = -1;
+ cp->flags = NULL;
+ cp->logfp = NULL;
+ cp->wq.size = 0;
+ cp->wq.buf = NULL;
+ cp->wq.len = 0;
+ cp->wq.offset = 0;
+ cp->next = connections;
+ cp->errstr = NULL;
+ connections = cp;
+ return cp;
+}
+
+
+static void print_connections(void)
+{
+ if (debug) {
+ Connection *cp = connections;
+ DEBUGF(("CONNECTIONS:\n"));
+ while (cp) {
+ if (cp->state == ESOCK_JOINED) {
+ DEBUGF((" - %s [%8p] (origin = %s)\n"
+ " (fd = %d, eof = %d, wq = %d, bp = %d)\n"
+ " (proxyfd = %d, eof = %d, wq = %d, bp = %d)\n",
+ connstr[cp->state], cp, originstr[cp->origin],
+ cp->fd, cp->eof, cp->wq.len, cp->bp,
+ cp->proxy->fd, cp->proxy->eof, cp->proxy->wq.len,
+ cp->proxy->bp));
+ } else if (cp->state == ESOCK_ACTIVE_LISTENING) {
+ DEBUGF((" - %s [%8p] (fd = %d, acceptors = %d)\n",
+ connstr[cp->state], cp, cp->fd, cp->acceptors));
+ } else {
+ DEBUGF((" - %s [%8p] (fd = %d)\n", connstr[cp->state], cp,
+ cp->fd));
+ }
+ cp= cp->next;
+ }
+ }
+}
+
+static void dump_connections(void)
+{
+ Connection *cp = connections;
+ Proxy *pp = proxies;
+ time_t t = time(NULL);
+ int length = 0;
+ struct sockaddr_in iserv_addr;
+
+ __debugprintf("CONNECTIONS %s", ctime(&t));
+ while (cp) {
+ if (cp->state == ESOCK_JOINED) {
+ __debugprintf(" - %s [%8p] (origin = %s)\n"
+ " (fd = %d, eof = %d, wq = %d, bp = %d), close = %d\n"
+ " (proxyfd = %d, eof = %d, wq = %d, bp = %d)\n",
+ connstr[cp->state], cp, originstr[cp->origin],
+ cp->fd, cp->eof, cp->wq.len, cp->bp, cp->close,
+ cp->proxy->fd, cp->proxy->eof, cp->proxy->wq.len,
+ cp->proxy->bp);
+ } else if (cp->state == ESOCK_ACTIVE_LISTENING) {
+ __debugprintf(" - %s [%8p] (fd = %d, acceptors = %d)\n",
+ connstr[cp->state], cp, cp->fd, cp->acceptors);
+ } else {
+ __debugprintf(" - %s [%8p] (fd = %d)\n", connstr[cp->state], cp,
+ cp->fd);
+ }
+ length = sizeof(iserv_addr);
+ if ((cp->state == ESOCK_ACTIVE_LISTENING) ||
+ (cp->state == ESOCK_PASSIVE_LISTENING)) {
+ getsockname(cp->fd, (struct sockaddr *) &iserv_addr, &length);
+ __debugprintf(" (ip = %s, port = %d)\n",
+ inet_ntoa(iserv_addr.sin_addr),
+ ntohs(iserv_addr.sin_port));
+ }
+ else {
+ getsockname(cp->fd, (struct sockaddr *) &iserv_addr, &length);
+ __debugprintf(" (local_ip = %s, local_port = %d)\n",
+ inet_ntoa(iserv_addr.sin_addr),
+ ntohs(iserv_addr.sin_port));
+ length = sizeof(iserv_addr);
+ getpeername(cp->fd, (struct sockaddr *) &iserv_addr, &length);
+ __debugprintf(" (remote_ip = %s, remote_port = %d)\n",
+ inet_ntoa(iserv_addr.sin_addr),
+ ntohs(iserv_addr.sin_port));
+ }
+ cp=cp->next;
+ }
+
+ __debugprintf("PROXIES\n");
+ while (pp) {
+ __debugprintf(" - fd = %d [%8p] (external_fd = %d, peer_port = %d,"
+ " eof = %d)\n", pp->fd, pp, pp->conn->fd, pp->peer_port,
+ pp->eof);
+
+ pp= pp->next;
+ }
+}
+
+static Connection *get_connection(FD fd)
+{
+ Connection *cp = connections;
+
+ while(cp) {
+ if(cp->fd == fd)
+ return cp;
+ cp = cp->next;
+ }
+ return NULL;
+}
+
+/*
+ * Remove a connection from the list of connection, close the proxy
+ * socket and free all resources. The main socket (fd) is *not*
+ * closed here, because the closing of that socket has to be synchronized
+ * with the Erlang process controlling this port program.
+ */
+static void remove_connection(Connection *conn)
+{
+ Connection **prev = &connections;
+ Connection *cp = connections;
+
+ while (cp) {
+ if(cp == conn) {
+ DEBUGF(("remove_connection: fd = %d\n", cp->fd));
+ esock_ssl_free(cp); /* frees cp->opaque only */
+ esock_free(cp->flags);
+ closelog(cp->logfp); /* XXX num_sock_fds */
+ esock_free(cp->wq.buf);
+ if (cp->proxy) {
+ safe_close(cp->proxy->fd);
+ remove_proxy(cp->proxy);
+ }
+ *prev = cp->next;
+ esock_free(cp);
+ return;
+ }
+ prev = &cp->next;
+ cp = cp->next;
+ }
+}
+
+static Proxy *get_proxy_by_peerport(int port)
+{
+ Proxy *p = proxies;
+
+ while(p) {
+ if (p->peer_port == port)
+ return p;
+ p = p->next;
+ }
+ return NULL;
+}
+
+static Proxy *new_proxy(FD fd)
+{
+ Proxy *p;
+
+ if (!(p = esock_malloc(sizeof(Proxy))))
+ return NULL;
+
+ p->fd = fd;
+ p->peer_port = -1;
+ p->eof = 0;
+ p->bp = 0;
+ p->conn = NULL;
+ p->wq.size = 0;
+ p->wq.buf = NULL;
+ p->wq.len = 0;
+ p->wq.offset = 0;
+ p->next = proxies;
+ proxies = p;
+ return p;
+}
+
+static void remove_proxy(Proxy *proxy)
+{
+ Proxy *p = proxies, **pp = &proxies;
+
+ while(p) {
+ if (p == proxy) {
+ DEBUGF(("remove_proxyfd = %d\n", p->fd));
+ esock_free(p->wq.buf);
+ *pp = p->next;
+ esock_free(p);
+ return;
+ }
+ pp = &p->next;
+ p = p->next;
+ }
+}
+
+static int check_num_sock_fds(FD fd)
+{
+ num_sock_fds++; /* fd is valid */
+#ifdef USE_SELECT
+ if (num_sock_fds > FD_SETSIZE) {
+ num_sock_fds--;
+ sock_set_errno(ERRNO_MFILE);
+ safe_close(fd);
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+static void safe_close(FD fd)
+{
+ int err;
+
+ err = sock_errno();
+ DEBUGF(("safe_close fd = %d\n", fd));
+ if (sock_close(fd) < 0) {
+ DEBUGF(("safe_close failed\n"));
+ } else {
+ num_sock_fds--;
+ }
+ sock_set_errno(err);
+}
+
+static void clean_up(void)
+{
+ Connection *cp, *cpnext;
+ Proxy *pp, *ppnext;
+
+ cp = connections;
+ while (cp) {
+ safe_close(cp->fd);
+ cpnext = cp->next;
+ remove_connection(cp);
+ cp = cpnext;
+ }
+
+ pp = proxies;
+ while (pp) {
+ safe_close(pp->fd);
+ ppnext = pp->next;
+ remove_proxy(pp);
+ pp = ppnext;
+ }
+}
+
+static void ensure_write_queue(WriteQueue *wq, int size)
+{
+ if (wq->size < size) {
+ wq->buf = esock_realloc(wq->buf, size);
+ wq->size = size;
+ }
+}
+
+
+
+
+
+
+
diff --git a/lib/ssl/c_src/esock.h b/lib/ssl/c_src/esock.h
new file mode 100644
index 0000000000..16c9faa530
--- /dev/null
+++ b/lib/ssl/c_src/esock.h
@@ -0,0 +1,273 @@
+/*<copyright>
+ * <year>1999-2008</year>
+ * <holder>Ericsson AB, All Rights Reserved</holder>
+ *</copyright>
+ *<legalnotice>
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson AB.
+ *</legalnotice>
+ */
+/*
+ * Purpose: Implementation of Secure Socket Layer (SSL).
+ *
+ */
+
+#ifndef ESOCK_H
+#define ESOCK_H
+
+#ifdef __WIN32__
+#include "esock_winsock.h"
+#endif
+#include <stdio.h>
+
+#ifdef __WIN32__
+#define INVALID_FD INVALID_SOCKET
+
+#define sock_read(fd, buf, len) recv((fd), (buf), (len), 0)
+#define sock_write(fd, buf, len) send((fd), (buf), (len), 0)
+#define sock_close(fd) closesocket(fd)
+#define sock_errno() WSAGetLastError()
+#define sock_set_errno(err) WSASetLastError(err)
+
+#define ERRNO_NONE 0
+#define ERRNO_BLOCK WSAEWOULDBLOCK
+#define ERRNO_CONNREFUSED WSAECONNREFUSED
+#define ERRNO_PROGRESS WSAEINPROGRESS
+#define ERRNO_PROTONOSUPPORT WSAEPROTONOSUPPORT
+#define ERRNO_INVAL WSAEINVAL
+#define ERRNO_ADDRNOTAVAIL WSAEADDRNOTAVAIL
+#define ERRNO_NOTSOCK WSAENOTSOCK
+#define ERRNO_OPNOTSUPP WSAEOPNOTSUPP
+#define ERRNO_MFILE WSAEMFILE
+#define SET_BLOCKING(fd) do { \
+ unsigned long zeroval = 0; \
+ ioctlsocket((fd), FIONBIO, &zeroval); \
+ } while (0)
+#define SET_NONBLOCKING(fd) do { \
+ unsigned long oneval = 1; \
+ ioctlsocket((fd), FIONBIO, &oneval); \
+ } while (0)
+#else
+#define INVALID_FD (-1)
+
+#define sock_read(fd, buf, len) read((fd), (buf), (len))
+#define sock_write(fd, buf, len) write((fd), (buf), (len))
+#define sock_close(fd) close(fd)
+#define sock_errno() errno
+#define sock_set_errno(err) do {errno = (err);} while(0)
+
+#define ERRNO_NONE 0
+#define ERRNO_BLOCK EAGAIN
+#define ERRNO_CONNREFUSED ECONNREFUSED
+#define ERRNO_PROGRESS EINPROGRESS
+#define ERRNO_PROTONOSUPPORT EPROTONOSUPPORT
+#define ERRNO_INVAL EINVAL
+#define ERRNO_ADDRNOTAVAIL EADDRNOTAVAIL
+#define ERRNO_NOTSOCK ENOTSOCK
+#define ERRNO_OPNOTSUPP EOPNOTSUPP
+#define ERRNO_MFILE EMFILE
+#define SET_BLOCKING(fd) fcntl((fd), F_SETFL, \
+ fcntl((fd), F_GETFL, 0) & ~O_NONBLOCK)
+#define SET_NONBLOCKING(fd) fcntl((fd), F_SETFL, \
+ fcntl((fd), F_GETFL, 0) | O_NONBLOCK)
+#endif
+
+#define GET_INT8(s) ((s)[0])
+#define GET_INT16(s) (((s)[0] << 8) | (s)[1])
+#define GET_INT32(s) (((s)[0] << 24) | ((s)[1] << 16) | \
+ ((s)[2] << 8) | (s)[3])
+
+#define PUT_INT8(x, s) do { (s)[0] = x; } while(0)
+#define PUT_INT16(x, s) do { (s)[0] = ((x) >> 8) & 0xff; \
+ (s)[1] = ((x) & 0xff); } while(0)
+#define PUT_INT32(x, s) do { (s)[0] = ((x) >> 24) & 0xff; \
+ (s)[1] = ((x) >> 16) & 0xff; \
+ (s)[2] = ((x) >> 8) & 0xff; \
+ (s)[3] = (x) & 0xff; } while(0)
+
+/* type for Connections */
+#define ESOCK_STATE_NONE 0
+#define ESOCK_ACTIVE_LISTENING 1
+#define ESOCK_PASSIVE_LISTENING 2
+#define ESOCK_CONNECTED 3
+#define ESOCK_WAIT_CONNECT 4
+#define ESOCK_SSL_CONNECT 5
+#define ESOCK_SSL_ACCEPT 6
+#define ESOCK_TRANSPORT_ACCEPT 7
+#define ESOCK_JOINED 8
+#define ESOCK_SSL_SHUTDOWN 9
+#define ESOCK_DEFUNCT 10
+
+#ifdef __WIN32__
+ typedef SOCKET FD;
+#else
+ typedef int FD;
+#endif
+
+/* For the shutdown(fd, how) call */
+#ifdef __WIN32__
+#define SHUTDOWN_READ SD_RECEIVE
+#define SHUTDOWN_WRITE SD_SEND
+#define SHUTDOWN_ALL SD_BOTH
+#else
+#define SHUTDOWN_READ 0
+#define SHUTDOWN_WRITE 1
+#define SHUTDOWN_ALL 2
+#endif
+
+#define ORIG_LISTEN 0
+#define ORIG_ACCEPT 1
+#define ORIG_CONNECT 2
+
+typedef struct {
+ int size; /* Total size of buf */
+ unsigned char *buf;
+ int len; /* Current number of bytes in buf */
+ int offset; /* Bytes already written */
+} WriteQueue;
+
+typedef struct _proxy Proxy;
+
+typedef struct Connection {
+ FD fd;
+ FD listen_fd; /* Needed for async listen error */
+ unsigned char state;
+ int acceptors; /* Count acceptors for listen socket */
+ Proxy *proxy;
+ void *opaque; /* Any suitable ssl structure */
+ int ssl_want; /* read/write flags */
+ int eof; /* end of file (read) */
+ int bp; /* broken pipe (write) */
+ int clean; /* Clean SSL shutdown initiated */
+ int close; /* Close if set */
+ int origin; /* listen, accept or connect */
+ int encrypted; /* 1 = SSL encrypted, 0 = normal, unencrypted tcp */
+ char *flags; /* ssl parameters */
+ FILE *logfp; /* connection log file (not used) */
+ WriteQueue wq;
+ struct Connection* next;
+ const char* errstr; /* only used to report errors from ssl_accept_init in SSL_ACCEPT */
+} Connection;
+
+struct _proxy {
+ FD fd;
+ int peer_port;
+ int eof; /* end of file (read) */
+ int bp; /* broken pipe (write) */
+ Connection *conn;
+ WriteQueue wq;
+ Proxy *next;
+};
+
+/* Commands, replies, and error responses */
+
+#define ESOCK_CONNECT_CMD 1
+#define ESOCK_CONNECT_WAIT_REP 2
+#define ESOCK_CONNECT_REP 3
+#define ESOCK_CONNECT_ERR 4
+
+#define ESOCK_TERMINATE_CMD 5
+#define ESOCK_CLOSE_CMD 6
+
+#define ESOCK_LISTEN_CMD 7
+#define ESOCK_LISTEN_REP 8
+#define ESOCK_LISTEN_ERR 9
+
+#define ESOCK_TRANSPORT_ACCEPT_CMD 10
+#define ESOCK_NOACCEPT_CMD 11
+#define ESOCK_TRANSPORT_ACCEPT_REP 12
+#define ESOCK_TRANSPORT_ACCEPT_ERR 13
+
+#define ESOCK_FROMNET_CLOSE_REP 14
+
+#define ESOCK_CONNECT_SYNC_ERR 15
+#define ESOCK_LISTEN_SYNC_ERR 16
+
+#define ESOCK_PROXY_PORT_REP 23
+#define ESOCK_PROXY_JOIN_CMD 24
+#define ESOCK_PROXY_JOIN_REP 25
+#define ESOCK_PROXY_JOIN_ERR 26
+
+#define ESOCK_SET_SOCKOPT_CMD 27
+#define ESOCK_IOCTL_OK 28
+#define ESOCK_IOCTL_ERR 29
+
+#define ESOCK_GETPEERNAME_CMD 30
+#define ESOCK_GETPEERNAME_REP 31
+#define ESOCK_GETPEERNAME_ERR 32
+
+#define ESOCK_GETSOCKNAME_CMD 33
+#define ESOCK_GETSOCKNAME_REP 34
+#define ESOCK_GETSOCKNAME_ERR 35
+
+#define ESOCK_GETPEERCERT_CMD 36
+#define ESOCK_GETPEERCERT_REP 37
+#define ESOCK_GETPEERCERT_ERR 38
+
+#define ESOCK_GETVERSION_CMD 39
+#define ESOCK_GETVERSION_REP 40
+
+#define ESOCK_SET_SEED_CMD 41
+
+#define ESOCK_GETCONNINFO_CMD 42
+#define ESOCK_GETCONNINFO_REP 43
+#define ESOCK_GETCONNINFO_ERR 44
+
+#define ESOCK_SSL_ACCEPT_CMD 45
+#define ESOCK_SSL_ACCEPT_REP 46
+#define ESOCK_SSL_ACCEPT_ERR 47
+
+#define ESOCK_DUMP_STATE_CMD 48
+#define ESOCK_SET_DEBUG_CMD 49
+#define ESOCK_SET_DEBUGMSG_CMD 50
+
+
+/* Option codes for ESOCK_SET_SOCKOPT_CMD */
+#define ESOCK_SET_TCP_NODELAY 1
+
+/* SSL want to read or write */
+#define ESOCK_SSL_WANT_READ 1
+#define ESOCK_SSL_WANT_WRITE 2
+
+/* Protocol version according to ssl_server */
+#define ESOCK_SSLv2 1
+#define ESOCK_SSLv3 2
+#define ESOCK_TLSv1 4
+
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/ssl/c_src/esock_openssl.c b/lib/ssl/c_src/esock_openssl.c
new file mode 100644
index 0000000000..2621c9934e
--- /dev/null
+++ b/lib/ssl/c_src/esock_openssl.c
@@ -0,0 +1,1213 @@
+/*<copyright>
+ * <year>1999-2008</year>
+ * <holder>Ericsson AB, All Rights Reserved</holder>
+ *</copyright>
+ *<legalnotice>
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson AB.
+ *</legalnotice>
+ */
+/*
+ * Purpose: Adaptions for the OpenSSL package.
+ *
+ * This file implements the functions defined in esock_ssl.h for
+ * the OpenSSL package.
+ *
+ * The following holds true for non-blockling I/O:
+ *
+ * Function Return values
+ * -------- -------------
+ * SSL_accept() success: 1, failure: =<0
+ * SSL_connect() success: 1, failure: =<0
+ * SSL_read() success: >0, eof: 0, failure: <0
+ * SSL_write() success: > 0, failure: =<0
+ * SSL_shutdown() success: 1, not finished: 0
+ *
+ * If the return value of any of the above functions is `ret' and the
+ * ssl connection is `ssl', the call
+ *
+ * ssl_error = SSL_get_error(ssl, ret);
+ *
+ * returns one of the following eight values:
+ *
+ * SSL_ERROR_NONE ret > 0
+ * SSL_ERROR_ZERO_RETURN ret = 0
+ * SSL_ERROR_WANT_READ ret < 0 and ssl wants to read
+ * SSL_ERROR_WANT_WRITE ret < 0 and ssl wants to write
+ * SSL_ERROR_SYSCALL ret < 0 or ret = 0
+ * SSL_ERROR_SSL if there was an ssl internal error
+ * SSL_ERROR_WANT_X509_LOOKUP ret < 0 and ssl wants x509 lookup
+ * SSL_ERROR_WANT_CONNECT ret < 0 and ssl wants connect
+ *
+ * It is the case that SSL_read() sometimes returns -1, even when the
+ * underlying file descriptor is ready for reading.
+ *
+ * Also, sometimes we may have SSL_ERROR_SSL in SSL_accept() and SSL_connect()
+ * when a retry should be done.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#ifndef __WIN32__
+# include <fcntl.h>
+# include <unistd.h>
+#endif
+
+#include "esock.h"
+#include "esock_ssl.h"
+#include "debuglog.h"
+#include "esock_utils.h"
+#include "esock_posix_str.h"
+
+#include <openssl/crypto.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+int ephemeral_rsa = 0;
+int ephemeral_dh = 0; /* XXX Not used yet */
+int protocol_version = 0;
+
+char *esock_ssl_errstr = "";
+
+#define FLAGSBUFSIZE 512
+#define X509BUFSIZE 256
+#define DEFAULT_VERIFY_DEPTH 1
+
+#define SET_WANT(cp, ssl_error) \
+ switch((ssl_error)) { \
+ case SSL_ERROR_WANT_READ: \
+ (cp)->ssl_want = ESOCK_SSL_WANT_READ; \
+ break; \
+ case SSL_ERROR_WANT_WRITE: \
+ (cp)->ssl_want = ESOCK_SSL_WANT_WRITE; \
+ break; \
+ default: \
+ (cp)->ssl_want = 0; \
+ break; \
+ }
+
+#define RESET_ERRSTR() \
+ esock_ssl_errstr = "";
+
+#define MAYBE_SET_ERRSTR(s) \
+ if (!esock_ssl_errstr[0]) \
+ esock_ssl_errstr = (s);
+
+typedef struct {
+ int code;
+ char *text;
+} err_entry;
+
+typedef struct {
+ SSL_CTX *ctx;
+ char *passwd;
+ int verify_depth;
+} callback_data;
+
+static char *ssl_error_str(int error);
+static void end_ssl_call(int ret, Connection *cp, int ssl_error);
+static void check_shutdown(Connection *cp);
+static int set_ssl_parameters(Connection *cp, SSL_CTX *ctx);
+static int verify_callback(int ok, X509_STORE_CTX *ctx);
+static int passwd_callback(char *buf, int num, int rwflag, void *userdata);
+static void info_callback(const SSL *ssl, int where, int ret);
+static void callback_data_free(void *parent, void *ptr,
+ CRYPTO_EX_DATA *ad,
+ int idx, long arg1, void *argp);
+static RSA *tmp_rsa_callback(SSL *ssl, int is_export, int keylen);
+static void restrict_protocols(SSL_CTX *ctx);
+
+static err_entry errs[] = {
+ {SSL_ERROR_NONE, "SSL_ERROR_NONE"},
+ {SSL_ERROR_ZERO_RETURN, "SSL_ERROR_ZERO_RETURN"},
+ {SSL_ERROR_WANT_READ, "SSL_ERROR_WANT_READ"},
+ {SSL_ERROR_WANT_WRITE, "SSL_ERROR_WANT_WRITE"},
+ {SSL_ERROR_SYSCALL, "SSL_ERROR_SYSCALL"},
+ {SSL_ERROR_SSL, "SSL_ERROR_SSL"},
+ {SSL_ERROR_WANT_X509_LOOKUP, "SSL_ERROR_WANT_X509_LOOKUP"},
+ {SSL_ERROR_WANT_CONNECT, "SSL_ERROR_WANT_CONNECT"}
+};
+
+static SSL_METHOD *method; /* for listen and connect init */
+static char x509_buf[X509BUFSIZE]; /* for verify_callback */
+static int callback_data_index = -1; /* for ctx ex_data */
+static unsigned char randvec[1024]; /* XXX */
+
+#if defined(__WIN32__) || OPEN_MAX > 256
+# define FOPEN_WORKAROUND(var, expr) var = (expr)
+# define VOID_FOPEN_WORKAROUND(expr) expr
+#else
+/*
+ * This is an ugly workaround. On Solaris, fopen() will return NULL if
+ * it gets a file descriptor > 255. To avoid that, we'll make sure that
+ * there is always one low-numbered file descriptor available when
+ * fopen() is called.
+ */
+static int reserved_fd; /* Reserve a low-numbered file descriptor */
+# define USE_FOPEN_WORKAROUND 1
+
+# define FOPEN_WORKAROUND(var, expr) \
+do { \
+ close(reserved_fd); \
+ var = (expr); \
+ reserved_fd = open("/dev/null", O_RDONLY); \
+} while (0)
+
+# define VOID_FOPEN_WORKAROUND(expr) \
+do { \
+ close(reserved_fd); \
+ expr; \
+ reserved_fd = open("/dev/null", O_RDONLY); \
+} while (0)
+#endif
+
+esock_version *esock_ssl_version(void)
+{
+ static esock_version vsn;
+
+ vsn.compile_version = OPENSSL_VERSION_TEXT;
+ vsn.lib_version = SSLeay_version(SSLEAY_VERSION);
+ return &vsn;
+}
+
+char *esock_ssl_ciphers(void)
+{
+ SSL_CTX *ctx;
+ SSL *ssl;
+ char *ciphers;
+ const char *cp;
+ int i = 0, used = 0, len, incr = 1024;
+
+ if (!(ctx = SSL_CTX_new(method)))
+ return NULL;
+ restrict_protocols(ctx);
+ if (!(ssl = SSL_new(ctx))) {
+ SSL_CTX_free(ctx);
+ return NULL;
+ }
+
+ ciphers = esock_malloc(incr);
+ len = incr;
+ *ciphers = '\0';
+
+ while (1) {
+ if (!(cp = SSL_get_cipher_list(ssl, i)))
+ break;
+ if (i > 0) {
+ if (used == len) {
+ len += incr;
+ ciphers = esock_realloc(ciphers, len);
+ }
+ strcat(ciphers, ":");
+ used++;
+ }
+ if (strlen(cp) + used >= len) {
+ len += incr;
+ ciphers = esock_realloc(ciphers, len);
+ }
+ strcat(ciphers, cp);
+ used += strlen(cp);
+ i++;
+ }
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ return ciphers;
+}
+
+void esock_ssl_seed(void *buf, int len)
+{
+ RAND_seed(buf, len);
+
+ /* XXX Maybe we should call RAND_status() and check if we have got
+ * enough randomness.
+ */
+}
+
+int esock_ssl_init(void)
+{
+ method = SSLv23_method(); /* SSLv2, SSLv3 and TLSv1, may be restricted
+ in listen and connect */
+ SSL_load_error_strings();
+ SSL_library_init();
+ esock_ssl_seed(randvec, sizeof(randvec));
+ callback_data_index = SSL_CTX_get_ex_new_index(0, "callback_data",
+ NULL, NULL,
+ callback_data_free);
+#ifdef USE_FOPEN_WORKAROUND
+ reserved_fd = open("/dev/null", O_RDONLY);
+ DEBUGF(("init: reserved_fd=%d\r\n", reserved_fd));
+#endif
+ return 0;
+}
+
+
+void esock_ssl_finish(void)
+{
+ /* Nothing */
+}
+
+
+void esock_ssl_free(Connection *cp)
+{
+ SSL *ssl = cp->opaque;
+ SSL_CTX *ctx;
+
+ if (ssl) {
+ ctx = SSL_get_SSL_CTX(ssl);
+ SSL_free(ssl);
+ if (cp->origin != ORIG_ACCEPT)
+ SSL_CTX_free(ctx);
+ cp->opaque = NULL;
+ }
+}
+
+
+/*
+ * Print SSL specific errors.
+ */
+void esock_ssl_print_errors_fp(FILE *fp)
+{
+ ERR_print_errors_fp(fp);
+}
+
+
+int esock_ssl_accept_init(Connection *cp, void *listenssl)
+{
+ SSL_CTX *listenctx;
+ SSL *ssl;
+
+ RESET_ERRSTR();
+ MAYBE_SET_ERRSTR("esslacceptinit");
+
+ if(!listenssl) {
+ DEBUGF(("esock_ssl_accept_init: listenssl null\n"));
+ return -1;
+ }
+ if (!(listenctx = SSL_get_SSL_CTX(listenssl))) {
+ DEBUGF(("esock_ssl_accept_init: SSL_get_SSL_CTX\n"));
+ return -1;
+ }
+ if (!(ssl = cp->opaque = SSL_new(listenctx))) {
+ DEBUGF(("esock_ssl_accept_init: SSL_new(listenctx)\n"));
+ return -1;
+ }
+ SSL_set_fd(ssl, cp->fd);
+ return 0;
+
+}
+
+
+int esock_ssl_connect_init(Connection *cp)
+{
+ SSL_CTX *ctx;
+ SSL *ssl;
+
+ RESET_ERRSTR();
+ MAYBE_SET_ERRSTR("esslconnectinit");
+
+ if (!(ctx = SSL_CTX_new(method)))
+ return -1;
+ if (set_ssl_parameters(cp, ctx) < 0) {
+ SSL_CTX_free(ctx);
+ return -1;
+ }
+ restrict_protocols(ctx);
+ if (!(ssl = cp->opaque = SSL_new(ctx))) {
+ SSL_CTX_free(ctx);
+ return -1;
+ }
+ SSL_set_fd(ssl, cp->fd);
+ return 0;
+}
+
+
+int esock_ssl_listen_init(Connection *cp)
+{
+ SSL_CTX *ctx;
+ SSL *ssl;
+
+ RESET_ERRSTR();
+ MAYBE_SET_ERRSTR("essllisteninit");
+
+ if (!(ctx = SSL_CTX_new(method)))
+ return -1;
+ if (set_ssl_parameters(cp, ctx) < 0) {
+ SSL_CTX_free(ctx);
+ return -1;
+ }
+ restrict_protocols(ctx);
+
+ /* The allocation of ctx is for setting ssl parameters, so that
+ * accepts can inherit them. We allocate ssl to be able to
+ * refer to it via cp->opaque, but will not be used otherwise.
+ */
+ if (!(ssl = cp->opaque = SSL_new(ctx))) {
+ SSL_CTX_free(ctx);
+ return -1;
+ }
+ /* Set callback for temporary ephemeral RSA key generation.
+ * Note: for servers only. */
+ SSL_CTX_set_tmp_rsa_callback(ctx, tmp_rsa_callback);
+ return 0;
+}
+
+/*
+ * esock_ssl_accept(Connection *cp)
+ *
+ */
+int esock_ssl_accept(Connection *cp)
+{
+ int ret, ssl_error;
+ SSL *ssl = cp->opaque;
+
+ RESET_ERRSTR();
+
+ DEBUGF(("esock_ssl_accept: calling SSL_accept fd = %d\n"
+ " state before: %s\n", cp->fd, SSL_state_string(ssl)));
+ ret = SSL_accept(ssl);
+ DEBUGF((" sock_errno %d errno %d \n", sock_errno(), errno));
+ ssl_error = SSL_get_error(ssl, ret);
+ DEBUGF((" SSL_accept = %d\n"
+ " ssl_error: %s\n"
+ " state after: %s\n",
+ ret, ssl_error_str(ssl_error), SSL_state_string(ssl)));
+ DEBUGF((" ret %d os error %s\n", ret, strerror(errno)));
+ if (ret > 0)
+ return ret;
+ else if (ret == 0) {
+ const char* f; int l; unsigned int e;
+ while ((e = ERR_get_error_line(&f, &l))) {
+ DEBUGF((" error %s:%d %s\n", f, l, ssl_error_str(e)));
+ }
+ /* permanent accept error */
+ sock_set_errno(ERRNO_NONE);
+ MAYBE_SET_ERRSTR("esslaccept");
+ return -1;
+ }
+ end_ssl_call(ret, cp, ssl_error);
+ return ret;
+}
+
+/*
+ * esock_ssl_connect(Connection *cp)
+ *
+ */
+int esock_ssl_connect(Connection *cp)
+{
+ int ret, ssl_error;
+ SSL *ssl = cp->opaque;
+
+ RESET_ERRSTR();
+
+ DEBUGF(("esock_ssl_connect: calling SSL_connect fd = %d\n"
+ " state before: %s\n", cp->fd, SSL_state_string(ssl)));
+ ret = SSL_connect(ssl);
+ ssl_error = SSL_get_error(ssl, ret);
+ DEBUGF((" SSL_connect() = %d\n"
+ " ssl_error: %s\n"
+ " state after: %s\n",
+ ret, ssl_error_str(ssl_error), SSL_state_string(ssl)));
+ if (ret > 0)
+ return ret;
+ else if (ret == 0) {
+ /* permanent connect error */
+ sock_set_errno(ERRNO_NONE);
+ MAYBE_SET_ERRSTR("esslconnect");
+ return -1;
+ }
+ end_ssl_call(ret, cp, ssl_error);
+ return ret;
+}
+
+
+int esock_ssl_session_reused(Connection *cp)
+{
+ SSL *ssl = cp->opaque;
+
+ return SSL_session_reused(ssl);
+}
+
+
+/* esock_ssl_read(Connection *cp, char *buf, int len)
+ *
+ * Read at most `len' chars into `buf'. Returns number of chars
+ * read ( > 0), or 0 at EOF, or -1 on error. Sets cp->eof, cp->bp if
+ * appropriate.
+ */
+
+int esock_ssl_read(Connection *cp, char *buf, int len)
+{
+ int ret, ssl_error;
+ SSL *ssl = cp->opaque;
+
+ RESET_ERRSTR();
+ DEBUGF(("esock_ssl_read: calling SSL_read fd = %d\n"
+ " state before: %s\n", cp->fd, SSL_state_string(ssl)));
+
+ ret = SSL_read(ssl, buf, len);
+ ssl_error = SSL_get_error(ssl, ret);
+
+ DEBUGF((" SSL_read = %d\n"
+ " ssl_error: %s\n"
+ " state after: %s\n",
+ ret, ssl_error_str(ssl_error), SSL_state_string(ssl)));
+
+ if (ssl_error == SSL_ERROR_NONE) {
+ DEBUGMSGF(("message (hex) : [%3.*a]\n", ret, buf));
+ DEBUGMSGF(("message (char): [%3.*b]\n", ret, buf));
+ }
+ if (ret > 0)
+ return ret;
+ if (ret == 0) {
+ check_shutdown(cp);
+ return ret;
+ }
+ end_ssl_call(ret, cp, ssl_error);
+ return ret;
+}
+
+/*
+ * esock_ssl_write(Connection *cp, char *buf, int len)
+ *
+ * Writes at most `len' chars from `buf'. Returns number of chars
+ * written, or -1 on error.
+ */
+int esock_ssl_write(Connection *cp, char *buf, int len)
+{
+ int ret, ssl_error;
+ SSL *ssl = cp->opaque;
+
+ RESET_ERRSTR();
+ DEBUGF(("esock_ssl_write: calling SSL_write fd = %d\n"
+ " state before: %s\n", cp->fd, SSL_state_string(ssl)));
+ ret = SSL_write(ssl, buf, len);
+ ssl_error = SSL_get_error(ssl, ret);
+ DEBUGF((" SSL_write = %d\n"
+ " ssl_error: %s\n"
+ " state after: %s\n",
+ ret, ssl_error_str(ssl_error), SSL_state_string(ssl)));
+ if (ssl_error == SSL_ERROR_NONE) {
+ DEBUGMSGF(("message (hex) : [%3.*a]\n", ret, buf));
+ DEBUGMSGF(("message (char): [%3.*b]\n", ret, buf));
+ }
+ if (ret > 0)
+ return ret;
+ if (ret == 0) {
+ check_shutdown(cp);
+ return ret;
+ }
+ end_ssl_call(ret, cp, ssl_error);
+ return ret;
+}
+
+
+int esock_ssl_shutdown(Connection *cp)
+{
+ int ret, ssl_error;
+ SSL *ssl = cp->opaque;
+
+ RESET_ERRSTR();
+ DEBUGF(("esock_ssl_shutdown: calling SSL_shutdown fd = %d\n"
+ " state before: %s\n", cp->fd, SSL_state_string(ssl)));
+ ret = SSL_shutdown(ssl);
+ ssl_error = SSL_get_error(ssl, ret);
+ DEBUGF((" SSL_shutdown = %d\n"
+ " ssl_error: %s\n"
+ " state after: %s\n",
+ ret, ssl_error_str(ssl_error), SSL_state_string(ssl)));
+ if (ret >= 0) {
+ check_shutdown(cp);
+ return ret;
+ }
+ end_ssl_call(ret, cp, ssl_error);
+ return ret;
+}
+
+
+/* Returns total number of bytes in DER encoded cert pointed to by
+ * *buf, which is allocated by this function, unless return < 0.
+ * XXX X509_free ??
+ */
+int esock_ssl_getpeercert(Connection *cp, unsigned char **buf)
+{
+ int len;
+ SSL *ssl = cp->opaque;
+ X509 *x509;
+ unsigned char *tmp;
+
+ RESET_ERRSTR();
+ if((x509 = SSL_get_peer_certificate(ssl)) == NULL) {
+ MAYBE_SET_ERRSTR("enopeercert"); /* XXX doc */
+ return -1;
+ }
+
+ if ((len = i2d_X509(x509, NULL)) <= 0) {
+ MAYBE_SET_ERRSTR("epeercert");
+ return -1;
+ }
+
+ tmp = *buf = esock_malloc(len);
+
+ /* We must use a temporary value here, since i2d_X509(X509 *x,
+ * unsigned char **out) increments *out.
+ */
+ if (i2d_X509(x509, &tmp) < 0) {
+ esock_free(tmp);
+ MAYBE_SET_ERRSTR("epeercert");
+ return -1;
+ }
+ return len;
+}
+
+/* Returns total number of bytes in chain of certs. Each cert begins
+ * with a 4-bytes length. The last cert is ended with 4-bytes of
+ * zeros. The result is returned in *buf, which is allocated unless
+ * the return value is < 0.
+ * XXX X509_free ? sk_X509_free ?
+ * XXX X509_free is reference counting.
+ */
+int esock_ssl_getpeercertchain(Connection *cp, unsigned char **buf)
+{
+ SSL *ssl = cp->opaque;
+ STACK_OF(X509) *x509_stack;
+ X509 *x509;
+ int num, i, totlen, pos, *der_len;
+ unsigned char *vbuf;
+
+ RESET_ERRSTR();
+ if((x509_stack = SSL_get_peer_cert_chain(ssl)) == NULL) {
+ MAYBE_SET_ERRSTR("enopeercertchain"); /* XXX doc */
+ return -1;
+ }
+
+ num = sk_X509_num(x509_stack);
+ der_len = esock_malloc(num * sizeof(int));
+ totlen = 0;
+
+ for (i = 0; i < num; i++) {
+ x509 = sk_X509_value(x509_stack, i);
+ totlen += 4;
+ if ((der_len[i] = i2d_X509(x509, NULL)) < 0) {
+ MAYBE_SET_ERRSTR("epeercertchain");
+ esock_free(der_len);
+ return -1;
+ }
+ totlen += der_len[i];
+ }
+ totlen += 4;
+
+ vbuf = *buf = esock_malloc(totlen);
+ pos = 0;
+
+ for (i = 0; i < num; i++) {
+ x509 = sk_X509_value(x509_stack, i);
+ PUT_INT32(der_len[i], vbuf);
+ vbuf += 4;
+ /* Note: i2d_X509 increments vbuf */
+ if (i2d_X509(x509, &vbuf) < 0) {
+ MAYBE_SET_ERRSTR("epeercertchain");
+ esock_free(*buf);
+ esock_free(der_len);
+ return -1;
+ }
+ }
+ esock_free(der_len);
+ return totlen;
+}
+
+
+int esock_ssl_getprotocol_version(Connection *cp, char **buf)
+{
+ SSL *ssl = cp->opaque;
+
+ RESET_ERRSTR();
+ if (!ssl) {
+ MAYBE_SET_ERRSTR("enoent");
+ return -1;
+ }
+ *buf = (char *) SSL_get_version(ssl);
+
+ return 0;
+}
+
+
+int esock_ssl_getcipher(Connection *cp, char **buf)
+{
+ SSL *ssl = cp->opaque;
+
+ RESET_ERRSTR();
+ if (!ssl) {
+ MAYBE_SET_ERRSTR("enoent");
+ return -1;
+ }
+ *buf = (char *) SSL_get_cipher(ssl);
+
+ return 0;
+}
+
+/* Local functions */
+
+static char *ssl_error_str(int ssl_error)
+{
+ int i;
+ static char buf[128];
+
+ for (i = 0; i < sizeof(errs)/sizeof(err_entry); i ++) {
+ if (ssl_error == errs[i].code)
+ return errs[i].text;
+ }
+ sprintf(buf, "esock_openssl: SSL_error unknown: %d", ssl_error);
+ return buf;
+}
+
+void end_ssl_call(int ret, Connection *cp, int ssl_error)
+{
+ SET_WANT(cp, ssl_error);
+ switch (ssl_error) {
+ case SSL_ERROR_SYSCALL:
+ /* Typically sock_errno() is equal to ERRNO_BLOCK */
+ MAYBE_SET_ERRSTR(esock_posix_str(sock_errno()));
+ break;
+ case SSL_ERROR_SSL:
+ sock_set_errno(ERRNO_NONE);
+ MAYBE_SET_ERRSTR("esslerrssl");
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ SSLDEBUGF();
+ sock_set_errno(ERRNO_NONE);
+ MAYBE_SET_ERRSTR("ex509lookup");
+ break;
+ case SSL_ERROR_WANT_CONNECT:
+ SSLDEBUGF();
+ sock_set_errno(ERRNO_NONE);
+ MAYBE_SET_ERRSTR("ewantconnect");
+ break;
+ default:
+ break;
+ }
+}
+
+void check_shutdown(Connection *cp)
+{
+ int sd_mode;
+ SSL *ssl = cp->opaque;
+
+ sd_mode = SSL_get_shutdown(ssl);
+ if (sd_mode & SSL_RECEIVED_SHUTDOWN)
+ cp->eof = 1;
+ if (sd_mode & SSL_SENT_SHUTDOWN) {
+ DEBUGF(("check_shutdown SSL_SENT_SHUTDOWN\n"));
+ cp->bp = 1;
+ }
+}
+
+/*
+ * set_ssl_parameters
+ *
+ * Set ssl parameters from connection structure. Only called for
+ * listen and connect.
+ *
+ * Note: The -cacertdir option is not documented.
+ */
+static int set_ssl_parameters(Connection *cp, SSL_CTX *ctx)
+{
+ char *cacertfile = NULL, *cacertdir = NULL, *certfile = NULL;
+ char *keyfile = NULL, *ciphers = NULL, *password = NULL;
+ int verify = 0, verify_depth = DEFAULT_VERIFY_DEPTH, verify_mode;
+ int i, argc;
+ char **argv;
+ callback_data *cb_data;
+
+ RESET_ERRSTR();
+
+ argc = esock_build_argv(cp->flags, &argv);
+
+ DEBUGF(("Argv:\n"));
+ for (i = 0; i < argc; i++) {
+ DEBUGF(("%d: %s\n", i, argv[i]));
+ }
+
+ for (i = 0; i < argc; i++) {
+ if (strcmp(argv[i], "-verify") == 0) {
+ verify = atoi(argv[++i]);
+ } else if (strcmp(argv[i], "-depth") == 0) {
+ verify_depth = atoi(argv[++i]);
+ } else if (strcmp(argv[i], "-log") == 0) {
+ /* XXX ignored: logging per connection not supported */
+ i++;
+ } else if (strcmp(argv[i], "-certfile") == 0) {
+ certfile = argv[++i];
+ } else if (strcmp(argv[i], "-keyfile") == 0) {
+ keyfile = argv[++i];
+ } else if (strcmp(argv[i], "-password") == 0) {
+ password = argv[++i];
+ } else if (strcmp(argv[i], "-cacertfile") == 0) {
+ cacertfile = argv[++i];
+ } else if (strcmp(argv[i], "-cacertdir") == 0) {
+ cacertdir = argv[++i];
+ } else if (strcmp(argv[i], "-d") == 0) {
+ /* XXX ignored: debug per connection not supported */
+ i++;
+ } else if (strcmp(argv[i], "-ciphers") == 0) {
+ ciphers = argv[++i];
+ } else {
+ /* XXX Error: now ignored */
+ }
+ }
+ DEBUGF(("set_ssl_parameters: all arguments read\n"));
+
+ if (cp->origin == ORIG_LISTEN && !certfile) {
+ DEBUGF(("ERROR: Server must have certificate\n"));
+ MAYBE_SET_ERRSTR("enoservercert");
+ goto err_end;
+ }
+
+ /* Define callback data */
+ /* XXX Check for NULL */
+ cb_data = esock_malloc(sizeof(callback_data));
+ cb_data->ctx = ctx;
+ if (password) {
+ cb_data->passwd = esock_malloc(strlen(password) + 1);
+ strcpy(cb_data->passwd, password);
+ } else
+ cb_data->passwd = NULL;
+ cb_data->verify_depth = verify_depth;
+ SSL_CTX_set_ex_data(ctx, callback_data_index, cb_data);
+
+ /* password callback */
+ SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
+ SSL_CTX_set_default_passwd_cb_userdata(ctx, cb_data);
+
+ /* Set location for "trusted" certificates */
+ if (cacertfile || cacertdir) {
+ int res;
+ DEBUGF(("set_ssl_parameters: SSL_CTX_load_verify_locations\n"));
+ FOPEN_WORKAROUND(res, SSL_CTX_load_verify_locations(ctx, cacertfile,
+ cacertdir));
+ if (!res) {
+ DEBUGF(("ERROR: Cannot load verify locations\n"));
+ MAYBE_SET_ERRSTR("ecacertfile");
+ goto err_end;
+ }
+ } else {
+ int res;
+ DEBUGF(("set_ssl_parameters: SSL_CTX_set_default_verify_paths\n"));
+ FOPEN_WORKAROUND(res, SSL_CTX_set_default_verify_paths(ctx));
+ if (!res) {
+ DEBUGF(("ERROR: Cannot set default verify paths\n"));
+ MAYBE_SET_ERRSTR("ecacertfile");
+ goto err_end;
+ }
+ }
+
+ /* For a server the following sets the list of CA distinguished
+ * names that it sends to its client when it requests the
+ * certificate from the client.
+ * XXX The names of certs in cacertdir ignored.
+ */
+ if (cp->origin == ORIG_LISTEN && cacertfile) {
+ DEBUGF(("set_ssl_parameters: SSL_CTX_set_client_CA_list\n"));
+ VOID_FOPEN_WORKAROUND(SSL_CTX_set_client_CA_list(ctx,
+ SSL_load_client_CA_file(cacertfile)));
+ if (!SSL_CTX_get_client_CA_list(ctx)) {
+ DEBUGF(("ERROR: Cannot set client CA list\n"));
+ MAYBE_SET_ERRSTR("ecacertfile");
+ goto err_end;
+ }
+ }
+
+ /* Use certificate file if key file has not been set. */
+ if (!keyfile)
+ keyfile = certfile;
+
+ if (certfile) {
+ int res;
+ DEBUGF(("set_ssl_parameters: SSL_CTX_use_certificate_file\n"));
+ FOPEN_WORKAROUND(res, SSL_CTX_use_certificate_file(ctx, certfile,
+ SSL_FILETYPE_PEM));
+ if (res <= 0) {
+ DEBUGF(("ERROR: Cannot set certificate file\n"));
+ MAYBE_SET_ERRSTR("ecertfile");
+ goto err_end;
+ }
+ }
+ if (keyfile) {
+ int res;
+ DEBUGF(("set_ssl_parameters: SSL_CTX_use_PrivateKey_file\n"));
+ FOPEN_WORKAROUND(res, SSL_CTX_use_PrivateKey_file(ctx, keyfile,
+ SSL_FILETYPE_PEM));
+ if (res <= 0) {
+ DEBUGF(("ERROR: Cannot set private key file\n"));
+ MAYBE_SET_ERRSTR("ekeyfile");
+ goto err_end;
+ }
+ }
+ if(certfile && keyfile) {
+ DEBUGF(("set_ssl_parameters: SSL_CTX_check_private_key\n"));
+ if (!SSL_CTX_check_private_key(ctx)) {
+ DEBUGF(("ERROR: Private key does not match the certificate\n"));
+ MAYBE_SET_ERRSTR("ekeymismatch");
+ goto err_end;
+ }
+ }
+
+ /* Ciphers */
+ if (ciphers) {
+ DEBUGF(("set_ssl_parameters: SSL_CTX_set_cipher_list\n"));
+ if (!SSL_CTX_set_cipher_list(ctx, ciphers)) {
+ DEBUGF(("ERROR: Cannot set cipher list\n"));
+ MAYBE_SET_ERRSTR("ecipher");
+ goto err_end;
+ }
+ }
+
+ /* Verify depth */
+ DEBUGF(("set_ssl_parameters: SSL_CTX_set_verify_depth (depth = %d)\n",
+ verify_depth));
+ SSL_CTX_set_verify_depth(ctx, verify_depth);
+
+ /* Verify mode and callback */
+ /* XXX Why precisely these modes? */
+ switch (verify) {
+ case 0:
+ verify_mode = SSL_VERIFY_NONE;
+ break;
+ case 1:
+ verify_mode = SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE;
+ break;
+ case 2:
+ verify_mode = SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE|
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ break;
+ default:
+ verify_mode = SSL_VERIFY_NONE;
+ }
+ DEBUGF(("set_ssl_parameters: SSL_CTX_set_verify (verify = %d)\n",
+ verify));
+ SSL_CTX_set_verify(ctx, verify_mode, verify_callback);
+
+ /* Session id context. Should be an option really. */
+ if (cp->origin == ORIG_LISTEN) {
+ unsigned char *sid = "Erlang/OTP/ssl";
+ SSL_CTX_set_session_id_context(ctx, sid, strlen(sid));
+ }
+
+ /* info callback */
+ if (debug)
+ SSL_CTX_set_info_callback(ctx, info_callback);
+
+ DEBUGF(("set_ssl_parameters: done\n"));
+ /* Free arg list */
+ for (i = 0; argv[i]; i++)
+ esock_free(argv[i]);
+ esock_free(argv);
+ return 0;
+
+ err_end:
+ DEBUGF(("set_ssl_parameters: error\n"));
+ /* Free arg list */
+ for (i = 0; argv[i]; i++)
+ esock_free(argv[i]);
+ esock_free(argv);
+ return -1;
+}
+
+/* Call back functions */
+
+static int verify_callback(int ok, X509_STORE_CTX *x509_ctx)
+{
+ X509 *cert;
+ int cert_err, depth;
+ SSL *ssl;
+ SSL_CTX *ctx;
+ callback_data *cb_data;
+
+ cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+ cert_err = X509_STORE_CTX_get_error(x509_ctx);
+ depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+
+ ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+ SSL_get_ex_data_X509_STORE_CTX_idx());
+ ctx = SSL_get_SSL_CTX(ssl);
+ cb_data = SSL_CTX_get_ex_data(ctx, callback_data_index);
+
+ X509_NAME_oneline(X509_get_subject_name(cert), x509_buf, sizeof(x509_buf));
+ DEBUGF((" +vfy: depth = %d\n", depth));
+ DEBUGF((" subject = %s\n", x509_buf));
+ X509_NAME_oneline(X509_get_issuer_name(cert), x509_buf, sizeof(x509_buf));
+ DEBUGF((" issuer = %s\n", x509_buf));
+
+ if (!ok) {
+ DEBUGF((" +vfy: error = %d [%s]\n", cert_err,
+ X509_verify_cert_error_string(cert_err)));
+ if (depth >= cb_data->verify_depth)
+ ok = 1;
+ }
+
+ switch (cert_err) {
+ case X509_V_OK:
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ ok = 1;
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ MAYBE_SET_ERRSTR("enoissuercert");
+ break;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ MAYBE_SET_ERRSTR("epeercertexpired");
+ break;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ MAYBE_SET_ERRSTR("epeercertinvalid");
+ break;
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ MAYBE_SET_ERRSTR("eselfsignedcert");
+ break;
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ MAYBE_SET_ERRSTR("echaintoolong");
+ break;
+ default:
+ MAYBE_SET_ERRSTR("epeercert");
+ break;
+ }
+ DEBUGF((" +vfy: return = %d\n",ok));
+ return ok;
+}
+
+static int passwd_callback(char *buf, int num, int rwflag, void *userdata)
+{
+ callback_data *cb_data = userdata;
+ int len;
+
+ if (cb_data && cb_data->passwd) {
+ DEBUGF((" +passwd: %s\n", cb_data->passwd));
+ strncpy(buf, cb_data->passwd, num);
+ len = strlen(cb_data->passwd);
+ return len;
+ }
+ DEBUGF((" +passwd: ERROR: No password set.\n"));
+ return 0;
+}
+
+static void info_callback(const SSL *ssl, int where, int ret)
+{
+ char *str;
+
+ if (where & SSL_CB_LOOP) {
+ DEBUGF((" info: %s\n",SSL_state_string_long(ssl)));
+ } else if (where & SSL_CB_ALERT) {
+ str = (where & SSL_CB_READ) ? "read" : "write";
+ DEBUGF((" info: SSL3 alert %s:%s:%s\n", str,
+ SSL_alert_type_string_long(ret),
+ SSL_alert_desc_string_long(ret)));
+ } else if (where & SSL_CB_EXIT) {
+ if (ret == 0) {
+ DEBUGF((" info: failed in %s\n", SSL_state_string_long(ssl)));
+ } else if (ret < 0) {
+ DEBUGF((" info: error in %s\n", SSL_state_string_long(ssl)));
+ }
+ }
+}
+
+/* This function is called whenever a SSL_CTX *ctx structure is
+ * freed.
+*/
+static void callback_data_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
+ int idx, long arg1, void *argp)
+{
+ callback_data *cb_data = ptr;
+
+ if (cb_data) {
+ if (cb_data->passwd)
+ esock_free(cb_data->passwd);
+ esock_free(cb_data);
+ }
+}
+
+static RSA *tmp_rsa_callback(SSL *ssl, int is_export, int keylen)
+{
+ static RSA *rsa512 = NULL;
+ static RSA *rsa1024 = NULL;
+
+ switch (keylen) {
+ case 512:
+ if (!rsa512)
+ rsa512 = RSA_generate_key(keylen, RSA_F4, NULL, NULL);
+ return rsa512;
+ break;
+ case 1024:
+ if (!rsa1024)
+ rsa1024 = RSA_generate_key(keylen, RSA_F4, NULL, NULL);
+ return rsa1024;
+ break;
+ default:
+ if (rsa1024)
+ return rsa1024;
+ if (rsa512)
+ return rsa512;
+ rsa512 = RSA_generate_key(keylen, RSA_F4, NULL, NULL);
+ return rsa512;
+ }
+}
+
+/* Restrict protocols (SSLv2, SSLv3, TLSv1) */
+static void restrict_protocols(SSL_CTX *ctx)
+{
+ long options = 0;
+
+ if (protocol_version) {
+ if ((protocol_version & ESOCK_SSLv2) == 0)
+ options |= SSL_OP_NO_SSLv2;
+ if ((protocol_version & ESOCK_SSLv3) == 0)
+ options |= SSL_OP_NO_SSLv3;
+ if ((protocol_version & ESOCK_TLSv1) == 0)
+ options |= SSL_OP_NO_TLSv1;
+ SSL_CTX_set_options(ctx, options);
+ }
+}
+
+
+static unsigned char randvec [] = {
+ 181, 177, 237, 240, 107, 24, 43, 148,
+ 105, 4, 248, 13, 199, 255, 23, 58,
+ 71, 181, 57, 151, 156, 25, 165, 7,
+ 73, 80, 80, 231, 70, 110, 96, 162,
+ 24, 205, 178, 178, 67, 122, 210, 180,
+ 92, 6, 156, 182, 84, 159, 85, 6,
+ 175, 66, 165, 167, 137, 34, 179, 237,
+ 77, 90, 87, 185, 21, 106, 92, 115,
+ 137, 65, 233, 42, 164, 153, 208, 133,
+ 160, 172, 129, 202, 46, 220, 98, 66,
+ 115, 66, 46, 28, 226, 200, 140, 145,
+ 207, 194, 58, 71, 56, 203, 113, 34,
+ 221, 116, 63, 114, 188, 210, 45, 238,
+ 200, 123, 35, 150, 2, 78, 160, 22,
+ 226, 167, 162, 10, 182, 75, 109, 97,
+ 86, 252, 93, 125, 117, 214, 220, 37,
+ 105, 160, 56, 158, 97, 57, 22, 14,
+ 73, 169, 111, 190, 222, 176, 14, 82,
+ 111, 42, 87, 90, 136, 236, 22, 209,
+ 156, 207, 40, 251, 88, 141, 51, 211,
+ 31, 158, 153, 91, 119, 83, 255, 60,
+ 55, 94, 5, 115, 119, 210, 224, 185,
+ 163, 163, 5, 3, 197, 106, 110, 206,
+ 109, 132, 50, 190, 177, 133, 175, 129,
+ 225, 161, 156, 244, 77, 150, 99, 38,
+ 17, 111, 46, 230, 152, 64, 50, 164,
+ 19, 78, 3, 164, 169, 175, 104, 97,
+ 103, 158, 91, 168, 186, 191, 73, 88,
+ 118, 112, 41, 188, 219, 0, 198, 209,
+ 206, 7, 5, 169, 127, 180, 80, 74,
+ 124, 4, 4, 108, 197, 67, 204, 29,
+ 101, 95, 174, 147, 64, 163, 89, 160,
+ 10, 5, 56, 134, 209, 69, 209, 55,
+ 214, 136, 45, 212, 113, 85, 159, 133,
+ 141, 249, 75, 40, 175, 91, 142, 13,
+ 179, 179, 51, 0, 136, 63, 148, 175,
+ 103, 162, 8, 214, 4, 24, 59, 71,
+ 9, 185, 48, 127, 159, 165, 8, 8,
+ 135, 151, 92, 214, 132, 151, 204, 169,
+ 24, 112, 229, 59, 236, 81, 238, 64,
+ 150, 196, 97, 213, 140, 159, 20, 24,
+ 79, 210, 191, 53, 130, 33, 157, 87,
+ 16, 180, 175, 217, 56, 123, 115, 196,
+ 130, 6, 155, 37, 220, 80, 232, 129,
+ 240, 57, 199, 249, 196, 152, 28, 111,
+ 124, 192, 59, 46, 29, 21, 178, 51,
+ 156, 17, 248, 61, 254, 80, 201, 131,
+ 203, 59, 227, 191, 71, 121, 134, 181,
+ 55, 79, 130, 225, 246, 36, 179, 224,
+ 189, 243, 200, 75, 73, 41, 251, 41,
+ 71, 251, 78, 146, 99, 101, 104, 69,
+ 18, 122, 65, 24, 232, 84, 246, 242,
+ 209, 18, 241, 114, 3, 65, 177, 99,
+ 49, 99, 215, 59, 9, 175, 195, 11,
+ 25, 46, 43, 120, 109, 179, 159, 250,
+ 239, 246, 135, 78, 2, 238, 214, 237,
+ 64, 170, 50, 44, 68, 67, 111, 232,
+ 225, 230, 224, 124, 76, 32, 52, 158,
+ 151, 54, 184, 135, 122, 66, 211, 215,
+ 121, 90, 124, 158, 55, 73, 116, 137,
+ 240, 15, 38, 31, 183, 86, 93, 49,
+ 148, 184, 125, 250, 155, 216, 84, 246,
+ 27, 172, 141, 54, 80, 158, 227, 254,
+ 189, 164, 238, 229, 68, 26, 231, 11,
+ 198, 222, 15, 141, 98, 8, 124, 219,
+ 60, 125, 170, 213, 114, 24, 189, 65,
+ 80, 186, 71, 126, 223, 153, 20, 141,
+ 110, 73, 173, 218, 214, 63, 205, 177,
+ 132, 115, 184, 28, 122, 232, 210, 72,
+ 237, 41, 93, 17, 152, 95, 242, 138,
+ 79, 98, 47, 197, 36, 17, 137, 230,
+ 15, 73, 193, 1, 181, 123, 0, 186,
+ 185, 135, 142, 200, 139, 78, 57, 145,
+ 191, 32, 98, 250, 113, 188, 71, 32,
+ 205, 81, 219, 99, 60, 87, 42, 95,
+ 249, 252, 121, 125, 246, 230, 74, 162,
+ 73, 59, 179, 142, 178, 47, 163, 161,
+ 236, 14, 123, 219, 18, 6, 102, 140,
+ 215, 210, 76, 9, 119, 147, 252, 63,
+ 13, 51, 161, 172, 180, 116, 212, 129,
+ 116, 237, 38, 64, 213, 222, 35, 14,
+ 183, 237, 78, 204, 250, 250, 5, 41,
+ 142, 5, 207, 154, 65, 183, 108, 82,
+ 1, 43, 149, 233, 89, 195, 25, 233,
+ 4, 34, 19, 122, 16, 58, 121, 5,
+ 118, 168, 22, 213, 49, 226, 163, 169,
+ 21, 78, 179, 232, 125, 216, 198, 147,
+ 245, 196, 199, 138, 185, 167, 179, 82,
+ 175, 53, 6, 162, 5, 141, 180, 212,
+ 95, 201, 234, 169, 111, 175, 138, 197,
+ 177, 246, 154, 41, 185, 201, 134, 187,
+ 88, 99, 231, 23, 190, 36, 72, 174,
+ 244, 185, 205, 50, 230, 226, 210, 119,
+ 175, 107, 109, 244, 12, 122, 84, 51,
+ 146, 95, 68, 74, 76, 212, 221, 103,
+ 244, 71, 63, 133, 149, 233, 48, 3,
+ 176, 168, 6, 98, 88, 226, 120, 190,
+ 205, 249, 38, 157, 205, 148, 250, 203,
+ 147, 62, 195, 229, 219, 109, 177, 119,
+ 120, 43, 165, 99, 253, 210, 180, 32,
+ 227, 180, 174, 64, 156, 139, 251, 53,
+ 205, 132, 210, 208, 3, 199, 115, 64,
+ 59, 27, 249, 164, 224, 191, 124, 241,
+ 142, 10, 19, 120, 227, 46, 174, 231,
+ 48, 65, 41, 56, 51, 38, 185, 95,
+ 250, 182, 100, 40, 196, 124, 173, 119,
+ 162, 148, 170, 34, 51, 68, 175, 60,
+ 242, 201, 225, 34, 146, 157, 159, 0,
+ 144, 148, 82, 72, 149, 53, 201, 10,
+ 248, 206, 154, 126, 33, 153, 56, 48,
+ 5, 90, 194, 22, 251, 173, 211, 202,
+ 203, 253, 112, 147, 188, 200, 142, 206,
+ 206, 175, 233, 76, 93, 104, 125, 41,
+ 64, 145, 202, 53, 130, 251, 23, 90,
+ 28, 199, 13, 128, 185, 154, 53, 194,
+ 195, 55, 80, 56, 151, 216, 195, 138,
+ 7, 170, 143, 236, 74, 141, 229, 174,
+ 32, 165, 131, 68, 174, 104, 35, 143,
+ 183, 41, 80, 191, 120, 79, 166, 240,
+ 123, 55, 60, 2, 128, 56, 4, 199,
+ 122, 85, 90, 76, 246, 29, 13, 6,
+ 126, 229, 14, 203, 244, 73, 121, 42,
+ 169, 35, 44, 202, 18, 69, 153, 120,
+ 141, 77, 124, 191, 215, 18, 115, 187,
+ 108, 246, 135, 151, 225, 192, 50, 89,
+ 128, 45, 39, 253, 149, 234, 203, 84,
+ 51, 174, 15, 237, 17, 57, 76, 81,
+ 39, 107, 40, 36, 22, 52, 92, 39};
diff --git a/lib/ssl/c_src/esock_osio.c b/lib/ssl/c_src/esock_osio.c
new file mode 100644
index 0000000000..41c5271c16
--- /dev/null
+++ b/lib/ssl/c_src/esock_osio.c
@@ -0,0 +1,328 @@
+/*<copyright>
+ * <year>1999-2008</year>
+ * <holder>Ericsson AB, All Rights Reserved</holder>
+ *</copyright>
+ *<legalnotice>
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson AB.
+ *</legalnotice>
+ */
+/*
+ * Purpose: Std filedescriptors, break handler
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef __WIN32__
+#include "esock_winsock.h"
+#include <process.h>
+#include <io.h>
+#include <fcntl.h>
+#else
+#include <unistd.h>
+#include <signal.h>
+#endif
+
+#include "esock.h"
+#include "debuglog.h"
+#include "esock_utils.h"
+#include "esock_osio.h"
+
+#ifdef __WIN32__
+#define write _write
+#define read _read
+#define LOCALHOSTADDR "127.0.0.1"
+#define LOCBUFSIZE 1024
+#endif
+
+#define PACKET_SIZE 4
+#define EBUFSIZE 256
+
+FD local_read_fd = 0;
+
+static int inc_rbuf(int size);
+static void free_rbuf(void);
+static int read_fill(unsigned char *buf, int len);
+#ifdef __WIN32__
+static int create_local_thread(void);
+static DWORD WINAPI local_thread(LPVOID lpvParam);
+static BOOL WINAPI signal_handler(DWORD ctrl);
+#endif
+
+static unsigned char *rbuf = NULL;
+static int rbuf_malloced = 0;
+#ifdef __WIN32__
+static unsigned long one = 1, zero = 0;
+static int local_portno;
+static char *local_buf;
+#endif
+
+int set_break_handler(void)
+{
+#ifndef __WIN32__
+ struct sigaction act;
+
+ /* Ignore SIGPIPE signal */
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &act, NULL);
+ return 0;
+#else
+ SetConsoleCtrlHandler(signal_handler, TRUE);
+ return 0;
+#endif
+}
+
+
+#ifdef __WIN32__
+
+int set_binary_mode(void)
+{
+ _setmode(0, _O_BINARY);
+ _setmode(1, _O_BINARY);
+ return 0;
+}
+
+int esock_osio_init(void)
+{
+ return create_local_thread();
+}
+
+void esock_osio_finish(void)
+{
+ sock_close(local_read_fd);
+}
+
+#endif
+
+int read_ctrl(unsigned char **ebufp)
+{
+ int tbh, cc;
+ unsigned char *mbuf;
+
+ if (inc_rbuf(EBUFSIZE) < 0) {
+ fprintf(stderr, "read_ctrl: cannot alloc rbuf\n");
+ return -1;
+ }
+ cc = read_fill(rbuf, PACKET_SIZE);
+ if (cc < 0) {
+ free_rbuf();
+ return -1;
+ }
+ if (cc == 0) {
+ free_rbuf();
+ return -1; /* XXX 0 ?? */
+ }
+ tbh = GET_INT32(rbuf);
+
+ if (tbh > rbuf_malloced - 4) {
+ if (inc_rbuf(tbh + 4) < 0)
+ return -1;
+ }
+
+ mbuf = rbuf + PACKET_SIZE;
+ cc = read_fill(mbuf, tbh);
+ DEBUGF(("-----------------------------------\n"));
+ DEBUGF(("read_ctrl: cc = %d\n", cc));
+ if(cc > 0) {
+ DEBUGMSGF(("message (hex) : [%3.*a]\n", cc, mbuf));
+ DEBUGMSGF(("message (char): [%3.*b]\n", cc, mbuf));
+ }
+ *ebufp = mbuf;
+ return cc;
+}
+
+int write_ctrl(unsigned char *buf, int len)
+{
+ unsigned char lb[4];
+
+ PUT_INT32(len, lb);
+ DEBUGF(("write_ctrl: len = %d\n", len));
+ DEBUGMSGF(("message (hex) : [%3.*a] [%3.*a]\n", PACKET_SIZE, lb,
+ len, buf));
+ DEBUGMSGF(("message (char): [%3.*b] [%3.*b]\n", PACKET_SIZE, lb,
+ len, buf));
+
+ if (write(1, lb, PACKET_SIZE) != PACKET_SIZE) { /* XXX */
+ fprintf(stderr, "write_ctrl: Bad write \n");
+ return -1;
+ }
+ if (write(1, buf, len) != len) { /* XXX */
+ fprintf(stderr, "write_ctrl: Bad write \n");
+ return -1;
+ }
+ return len;
+}
+
+
+/*
+ * Local functions
+ *
+ */
+
+static int inc_rbuf(int size)
+{
+ unsigned char *nbuf;
+
+ if (rbuf_malloced >= size)
+ return 0;
+ if (rbuf != NULL)
+ nbuf = esock_realloc(rbuf, size);
+ else
+ nbuf = esock_malloc(size);
+ if(nbuf != NULL) {
+ rbuf = nbuf;
+ rbuf_malloced = size;
+ return 0;
+ }
+ return -1;
+}
+
+static void free_rbuf(void)
+{
+ if (rbuf != NULL) {
+ esock_free(rbuf);
+ rbuf = NULL;
+ rbuf_malloced = 0;
+ }
+}
+
+/* Fill buffer, return buffer length, 0 for EOF, < 0 for error. */
+
+static int read_fill(unsigned char *buf, int len)
+{
+ int i, got = 0;
+
+ do {
+ if ((i = sock_read(local_read_fd, buf+got, len-got)) <= 0)
+ return i;
+ got += i;
+ } while (got < len);
+ return len;
+}
+
+
+#ifdef __WIN32__
+
+/*
+ * This routine creates a local thread, which reads from standard input
+ * and writes to a socket.
+ */
+
+static int create_local_thread(void)
+{
+ struct sockaddr_in iserv_addr;
+ SOCKET tmpsock;
+ int length;
+ unsigned threadaddr;
+
+ local_buf = esock_malloc(LOCBUFSIZE);
+ if ((tmpsock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
+ fprintf(stderr, "create_local_thread could not create socket.\n");
+ return -1;
+ }
+ memset(&iserv_addr, 0, sizeof(iserv_addr));
+ iserv_addr.sin_family = AF_INET;
+ iserv_addr.sin_addr.s_addr = inet_addr(LOCALHOSTADDR);
+ iserv_addr.sin_port = htons(0); /* Have any port */
+
+ if (bind(tmpsock, (struct sockaddr *) &iserv_addr,
+ sizeof(iserv_addr)) < 0) {
+ fprintf(stderr, "create_local_thread could not bind.\n");
+ closesocket(tmpsock);
+ return -1;
+ }
+ listen(tmpsock, 1);
+ length = sizeof(iserv_addr);
+ if (getsockname(tmpsock, (struct sockaddr *) &iserv_addr, &length) < 0) {
+ fprintf(stderr, "create_local_thread could not getsockname.\n");
+ closesocket(tmpsock);
+ return -1;
+ }
+ local_portno = ntohs(iserv_addr.sin_port);
+
+ if (_beginthreadex(NULL, 0, local_thread, NULL, 0, &threadaddr) == 0) {
+ fprintf(stderr, "create_local_thread could not _beginthreadex().\n");
+ closesocket(tmpsock);
+ return -1;
+ }
+ local_read_fd = accept(tmpsock, (struct sockaddr *) NULL, (int *) NULL);
+ if (local_read_fd == INVALID_FD) {
+ fprintf(stderr, "create_local_thread could not accept.\n");
+ closesocket(tmpsock);
+ return -1;
+ }
+ closesocket(tmpsock);
+ return 0;
+}
+
+static DWORD WINAPI local_thread(LPVOID lpvParam)
+{
+ SOCKET sock;
+ struct hostent *host;
+ char hostname[64];
+ struct sockaddr_in iserv_addr;
+ unsigned long addr;
+ int len;
+ HANDLE thread;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ memset(&iserv_addr, 0, sizeof(struct sockaddr_in));
+ iserv_addr.sin_family = AF_INET;
+ iserv_addr.sin_addr.s_addr = inet_addr(LOCALHOSTADDR);
+ iserv_addr.sin_port = htons(local_portno);
+ if(connect(sock, (struct sockaddr*)&iserv_addr, sizeof iserv_addr) ==
+ SOCKET_ERROR) {
+ fprintf(stderr, "local_thread thread could not connect\n");
+ closesocket(sock);
+ return 0;
+ }
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+
+ /* read from 0 and write to sock */
+ while (1) {
+ if ((len = read(0, local_buf, LOCBUFSIZE)) <= 0) {
+ closesocket(sock);
+ close(0);
+ return 0;
+ }
+ if (send(sock, local_buf, len, 0) != len ) {
+ closesocket(sock);
+ close(0);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/* Signal handler */
+
+static BOOL WINAPI signal_handler(DWORD ctrl)
+{
+ switch (ctrl) {
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ break;
+ case CTRL_LOGOFF_EVENT:
+ if (!getenv("ERLSRV_SERVICE_NAME"))
+ return FALSE;
+ break;
+ default:
+ exit(1);
+ }
+ return TRUE;
+}
+
+#endif
diff --git a/lib/ssl/c_src/esock_osio.h b/lib/ssl/c_src/esock_osio.h
new file mode 100644
index 0000000000..8742c3b05b
--- /dev/null
+++ b/lib/ssl/c_src/esock_osio.h
@@ -0,0 +1,34 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1999-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 ESOCK_OSIO_H
+#define ESOCK_OSIO_H
+
+extern FD local_read_fd;
+
+#ifdef __WIN32__
+int set_binary_mode(void);
+int esock_osio_init(void);
+void esock_osio_finish(void);
+#endif
+int set_break_handler(void);
+int read_ctrl(unsigned char **ebufp);
+int write_ctrl(unsigned char *buf, int len);
+
+#endif
diff --git a/lib/ssl/c_src/esock_poll.c b/lib/ssl/c_src/esock_poll.c
new file mode 100644
index 0000000000..e982eba881
--- /dev/null
+++ b/lib/ssl/c_src/esock_poll.c
@@ -0,0 +1,222 @@
+/*<copyright>
+ * <year>2005-2008</year>
+ * <holder>Ericsson AB, All Rights Reserved</holder>
+ *</copyright>
+ *<legalnotice>
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson AB.
+ *</legalnotice>
+ */
+
+/*
+ * Purpose: Hide poll() and select() behind an API so that we
+ * can use either one.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef __WIN32__
+#include "esock_winsock.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#ifdef __WIN32__
+#include <process.h>
+#else
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#endif
+
+#include "esock.h"
+#include "esock_ssl.h"
+#include "esock_utils.h"
+#include "esock_poll.h"
+#include "debuglog.h"
+
+#if !defined(USE_SELECT)
+
+/* At least on FreeBSD, we need POLLRDNORM for normal files, not POLLIN. */
+/* Whether this is a bug in FreeBSD, I don't know. */
+#ifdef POLLRDNORM
+#define POLL_INPUT (POLLIN | POLLRDNORM)
+#else
+#define POLL_INPUT POLLIN
+#endif
+
+static void poll_fd_set(EsockPoll *ep, FD fd, short events)
+{
+ int i, j;
+ int prev_num_fds = ep->num_fds;
+
+ if (ep->num_fds <= fd) {
+ ep->num_fds = fd + 64;
+ ep->fd_to_poll = (int *) esock_realloc(ep->fd_to_poll,
+ ep->num_fds*sizeof(int));
+ for (j = prev_num_fds; j < ep->num_fds; j++)
+ ep->fd_to_poll[j] = -1;
+ }
+ i = ep->fd_to_poll[fd];
+ if (i > 0 && i < ep->active && ep->fds[i].fd == fd) {
+ /* Already present in poll array */
+ ep->fds[i].events |= events;
+ } else {
+ /* Append to poll array */
+ if (ep->active >= ep->allocated) {
+ ep->allocated *= 2;
+ ep->fds = (struct pollfd *)
+ esock_realloc(ep->fds, ep->allocated*sizeof(struct pollfd));
+ }
+ ep->fd_to_poll[fd] = ep->active;
+ ep->fds[ep->active].fd = fd;
+ ep->fds[ep->active].events = events;
+ ep->fds[ep->active].revents = 0;
+ ep->active++;
+ }
+}
+
+static int poll_is_set(EsockPoll *ep, FD fd, short mask)
+{
+ if (fd >= ep->num_fds) {
+ return 0;
+ } else {
+ int i = ep->fd_to_poll[fd];
+ return 0 <= i && i < ep->active && ep->fds[i].fd == fd &&
+ (ep->fds[i].revents & mask) != 0;
+ }
+}
+
+#endif
+
+void esock_poll_init(EsockPoll *ep)
+{
+#ifdef USE_SELECT
+ /* Nothing to do here */
+#else
+ ep->allocated = 2;
+ ep->fds = (struct pollfd *) esock_malloc(ep->allocated*sizeof(struct pollfd));
+ ep->num_fds = 1;
+ ep->fd_to_poll = esock_malloc(ep->num_fds*sizeof(int));
+#endif
+}
+
+void esock_poll_zero(EsockPoll *ep)
+{
+#ifdef USE_SELECT
+ FD_ZERO(&ep->readmask);
+ FD_ZERO(&ep->writemask);
+ FD_ZERO(&ep->exceptmask);
+#else
+ int i;
+
+ for (i = 0; i < ep->num_fds; i++)
+ ep->fd_to_poll[i] = -1;
+ ep->active = 0;
+#endif
+}
+
+void esock_poll_fd_set_read(EsockPoll *ep, FD fd)
+{
+#ifdef USE_SELECT
+ FD_SET(fd, &ep->readmask);
+#else
+ poll_fd_set(ep, fd, POLL_INPUT);
+#endif
+}
+
+void esock_poll_fd_set_write(EsockPoll *ep, FD fd)
+{
+#ifdef USE_SELECT
+ FD_SET(fd, &ep->writemask);
+#else
+ poll_fd_set(ep, fd, POLLOUT);
+#endif
+}
+
+int esock_poll_fd_isset_read(EsockPoll *ep, FD fd)
+{
+#ifdef USE_SELECT
+ return FD_ISSET(fd, &ep->readmask);
+#else
+ return poll_is_set(ep, fd, (POLL_INPUT|POLLHUP|POLLERR|POLLNVAL));
+#endif
+}
+
+int esock_poll_fd_isset_write(EsockPoll *ep, FD fd)
+{
+#ifdef USE_SELECT
+ return FD_ISSET(fd, &ep->writemask);
+#else
+ return poll_is_set(ep, fd, (POLLOUT|POLLHUP|POLLERR|POLLNVAL));
+#endif
+}
+
+#ifdef __WIN32__
+void esock_poll_fd_set_exception(EsockPoll *ep, FD fd)
+{
+ FD_SET(fd, &ep->exceptmask);
+}
+
+int esock_poll_fd_isset_exception(EsockPoll *ep, FD fd)
+{
+ return FD_ISSET(fd, &ep->exceptmask);
+}
+#endif
+
+int esock_poll(EsockPoll *ep, int seconds)
+{
+ int sret;
+
+#ifdef USE_SELECT
+ struct timeval tv;
+
+ tv.tv_sec = seconds;
+ tv.tv_usec = 0;
+ sret = select(FD_SETSIZE, &ep->readmask, &ep->writemask, &ep->exceptmask, &tv);
+ if (sret == 0) {
+ FD_ZERO(&ep->readmask);
+ FD_ZERO(&ep->writemask);
+ FD_ZERO(&ep->exceptmask);
+ }
+#else
+ sret = poll(ep->fds, ep->active, 1000*seconds);
+#endif
+ return sret;
+}
+
+void esock_poll_clear_event(EsockPoll* ep, FD fd)
+{
+#ifdef USE_SELECT
+ FD_CLR(fd, &ep->readmask);
+ FD_CLR(fd, &ep->writemask);
+ FD_CLR(fd, &ep->exceptmask);
+#else
+ int i = ep->fd_to_poll[fd];
+ if (i > 0 && ep->fds[i].fd == fd)
+ ep->fds[i].revents = 0;
+#endif
+}
diff --git a/lib/ssl/c_src/esock_poll.h b/lib/ssl/c_src/esock_poll.h
new file mode 100644
index 0000000000..639976dfa9
--- /dev/null
+++ b/lib/ssl/c_src/esock_poll.h
@@ -0,0 +1,60 @@
+/*<copyright>
+ * <year>2005-2008</year>
+ * <holder>Ericsson AB, All Rights Reserved</holder>
+ *</copyright>
+ *<legalnotice>
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson AB.
+ *</legalnotice>
+ */
+#ifndef ESOCK_POLL_SELECT_H
+#define ESOCK_POLL_SELECT_H
+
+#if !defined(USE_SELECT)
+#include <poll.h>
+#endif
+
+typedef struct esock_poll {
+#ifdef USE_SELECT
+ fd_set readmask;
+ fd_set writemask;
+ fd_set exceptmask;
+#else
+ int* fd_to_poll; /* Map from fd to index into poll
+ * descriptor array.
+ */
+ int num_fds; /* Number of entries in fd_to_poll. */
+ struct pollfd* fds; /* Array of poll descriptors. */
+ int allocated; /* Allocated number of fds. */
+ int active; /* Active number of fds */
+#endif
+} EsockPoll;
+
+void esock_poll_init(EsockPoll *ep);
+void esock_poll_zero(EsockPoll *ep);
+
+void esock_poll_fd_set_read(EsockPoll *ep, FD fd);
+void esock_poll_fd_set_write(EsockPoll *ep, FD fd);
+
+void esock_poll_clear_event(EsockPoll *ep, FD fd);
+
+int esock_poll_fd_isset_read(EsockPoll *ep, FD fd);
+int esock_poll_fd_isset_write(EsockPoll *ep, FD fd);
+
+#ifdef __WIN32__
+void esock_poll_fd_set_exception(EsockPoll *ep, FD fd);
+int esock_poll_fd_isset_exception(EsockPoll *ep, FD fd);
+#endif
+
+int esock_poll(EsockPoll *ep, int seconds);
+#endif
diff --git a/lib/ssl/c_src/esock_posix_str.c b/lib/ssl/c_src/esock_posix_str.c
new file mode 100644
index 0000000000..31062baaaf
--- /dev/null
+++ b/lib/ssl/c_src/esock_posix_str.c
@@ -0,0 +1,642 @@
+/*
+ * %ExternalCopyright%
+ */
+
+/*
+ * Original: tclPosixStr.c --
+ *
+ * This file contains procedures that generate strings
+ * corresponding to various POSIX-related codes, such
+ * as errno and signals.
+ *
+ * Copyright (c) 1991-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1996 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tclPosixStr.c 1.32 96/10/10 10:09:42
+ */
+
+/* Copy of erl_posix_str.c */
+
+#ifdef __WIN32__
+#include "esock_winsock.h"
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include "esock_posix_str.h"
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * esock_posix_str --
+ *
+ * Return a textual identifier for the given errno value.
+ *
+ * Results:
+ * This procedure returns a machine-readable textual identifier
+ * that corresponds to the current errno value (e.g. "eperm").
+ * The identifier is the same as the #define name in errno.h,
+ * except that it is in lowercase.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char errstrbuf[32];
+
+char *esock_posix_str(int error)
+{
+ switch (error) {
+#ifdef E2BIG
+ case E2BIG: return "e2big";
+#endif
+#ifdef EACCES
+ case EACCES: return "eacces";
+#endif
+#ifdef EADDRINUSE
+ case EADDRINUSE: return "eaddrinuse";
+#endif
+#ifdef EADDRNOTAVAIL
+ case EADDRNOTAVAIL: return "eaddrnotavail";
+#endif
+#ifdef EADV
+ case EADV: return "eadv";
+#endif
+#ifdef EAFNOSUPPORT
+ case EAFNOSUPPORT: return "eafnosupport";
+#endif
+#ifdef EAGAIN
+ case EAGAIN: return "eagain";
+#endif
+#ifdef EALIGN
+ case EALIGN: return "ealign";
+#endif
+#if defined(EALREADY) && (!defined(EBUSY) || (EALREADY != EBUSY ))
+ case EALREADY: return "ealready";
+#endif
+#ifdef EBADE
+ case EBADE: return "ebade";
+#endif
+#ifdef EBADF
+ case EBADF: return "ebadf";
+#endif
+#ifdef EBADFD
+ case EBADFD: return "ebadfd";
+#endif
+#ifdef EBADMSG
+ case EBADMSG: return "ebadmsg";
+#endif
+#ifdef EBADR
+ case EBADR: return "ebadr";
+#endif
+#ifdef EBADRPC
+ case EBADRPC: return "ebadrpc";
+#endif
+#ifdef EBADRQC
+ case EBADRQC: return "ebadrqc";
+#endif
+#ifdef EBADSLT
+ case EBADSLT: return "ebadslt";
+#endif
+#ifdef EBFONT
+ case EBFONT: return "ebfont";
+#endif
+#ifdef EBUSY
+ case EBUSY: return "ebusy";
+#endif
+#ifdef ECHILD
+ case ECHILD: return "echild";
+#endif
+#ifdef ECHRNG
+ case ECHRNG: return "echrng";
+#endif
+#ifdef ECOMM
+ case ECOMM: return "ecomm";
+#endif
+#ifdef ECONNABORTED
+ case ECONNABORTED: return "econnaborted";
+#endif
+#ifdef ECONNREFUSED
+ case ECONNREFUSED: return "econnrefused";
+#endif
+#ifdef ECONNRESET
+ case ECONNRESET: return "econnreset";
+#endif
+#if defined(EDEADLK) && (!defined(EWOULDBLOCK) || (EDEADLK != EWOULDBLOCK))
+ case EDEADLK: return "edeadlk";
+#endif
+#if defined(EDEADLOCK) && (!defined(EDEADLK) || (EDEADLOCK != EDEADLK))
+ case EDEADLOCK: return "edeadlock";
+#endif
+#ifdef EDESTADDRREQ
+ case EDESTADDRREQ: return "edestaddrreq";
+#endif
+#ifdef EDIRTY
+ case EDIRTY: return "edirty";
+#endif
+#ifdef EDOM
+ case EDOM: return "edom";
+#endif
+#ifdef EDOTDOT
+ case EDOTDOT: return "edotdot";
+#endif
+#ifdef EDQUOT
+ case EDQUOT: return "edquot";
+#endif
+#ifdef EDUPPKG
+ case EDUPPKG: return "eduppkg";
+#endif
+#ifdef EEXIST
+ case EEXIST: return "eexist";
+#endif
+#ifdef EFAULT
+ case EFAULT: return "efault";
+#endif
+#ifdef EFBIG
+ case EFBIG: return "efbig";
+#endif
+#ifdef EHOSTDOWN
+ case EHOSTDOWN: return "ehostdown";
+#endif
+#ifdef EHOSTUNREACH
+ case EHOSTUNREACH: return "ehostunreach";
+#endif
+#if defined(EIDRM) && (!defined(EINPROGRESS) || (EIDRM != EINPROGRESS))
+ case EIDRM: return "eidrm";
+#endif
+#ifdef EINIT
+ case EINIT: return "einit";
+#endif
+#ifdef EINPROGRESS
+ case EINPROGRESS: return "einprogress";
+#endif
+#ifdef EINTR
+ case EINTR: return "eintr";
+#endif
+#ifdef EINVAL
+ case EINVAL: return "einval";
+#endif
+#ifdef EIO
+ case EIO: return "eio";
+#endif
+#ifdef EISCONN
+ case EISCONN: return "eisconn";
+#endif
+#ifdef EISDIR
+ case EISDIR: return "eisdir";
+#endif
+#ifdef EISNAME
+ case EISNAM: return "eisnam";
+#endif
+#ifdef ELBIN
+ case ELBIN: return "elbin";
+#endif
+#ifdef EL2HLT
+ case EL2HLT: return "el2hlt";
+#endif
+#ifdef EL2NSYNC
+ case EL2NSYNC: return "el2nsync";
+#endif
+#ifdef EL3HLT
+ case EL3HLT: return "el3hlt";
+#endif
+#ifdef EL3RST
+ case EL3RST: return "el3rst";
+#endif
+#ifdef ELIBACC
+ case ELIBACC: return "elibacc";
+#endif
+#ifdef ELIBBAD
+ case ELIBBAD: return "elibbad";
+#endif
+#ifdef ELIBEXEC
+ case ELIBEXEC: return "elibexec";
+#endif
+#ifdef ELIBMAX
+ case ELIBMAX: return "elibmax";
+#endif
+#ifdef ELIBSCN
+ case ELIBSCN: return "elibscn";
+#endif
+#ifdef ELNRNG
+ case ELNRNG: return "elnrng";
+#endif
+#if defined(ELOOP) && (!defined(ENOENT) || (ELOOP != ENOENT))
+ case ELOOP: return "eloop";
+#endif
+#ifdef EMFILE
+ case EMFILE: return "emfile";
+#endif
+#ifdef EMLINK
+ case EMLINK: return "emlink";
+#endif
+#ifdef EMSGSIZE
+ case EMSGSIZE: return "emsgsize";
+#endif
+#ifdef EMULTIHOP
+ case EMULTIHOP: return "emultihop";
+#endif
+#ifdef ENAMETOOLONG
+ case ENAMETOOLONG: return "enametoolong";
+#endif
+#ifdef ENAVAIL
+ case ENAVAIL: return "enavail";
+#endif
+#ifdef ENET
+ case ENET: return "enet";
+#endif
+#ifdef ENETDOWN
+ case ENETDOWN: return "enetdown";
+#endif
+#ifdef ENETRESET
+ case ENETRESET: return "enetreset";
+#endif
+#ifdef ENETUNREACH
+ case ENETUNREACH: return "enetunreach";
+#endif
+#ifdef ENFILE
+ case ENFILE: return "enfile";
+#endif
+#ifdef ENOANO
+ case ENOANO: return "enoano";
+#endif
+#if defined(ENOBUFS) && (!defined(ENOSR) || (ENOBUFS != ENOSR))
+ case ENOBUFS: return "enobufs";
+#endif
+#ifdef ENOCSI
+ case ENOCSI: return "enocsi";
+#endif
+#if defined(ENODATA) && (!defined(ECONNREFUSED) || (ENODATA != ECONNREFUSED))
+ case ENODATA: return "enodata";
+#endif
+#ifdef ENODEV
+ case ENODEV: return "enodev";
+#endif
+#ifdef ENOENT
+ case ENOENT: return "enoent";
+#endif
+#ifdef ENOEXEC
+ case ENOEXEC: return "enoexec";
+#endif
+#ifdef ENOLCK
+ case ENOLCK: return "enolck";
+#endif
+#ifdef ENOLINK
+ case ENOLINK: return "enolink";
+#endif
+#ifdef ENOMEM
+ case ENOMEM: return "enomem";
+#endif
+#ifdef ENOMSG
+ case ENOMSG: return "enomsg";
+#endif
+#ifdef ENONET
+ case ENONET: return "enonet";
+#endif
+#ifdef ENOPKG
+ case ENOPKG: return "enopkg";
+#endif
+#ifdef ENOPROTOOPT
+ case ENOPROTOOPT: return "enoprotoopt";
+#endif
+#ifdef ENOSPC
+ case ENOSPC: return "enospc";
+#endif
+#if defined(ENOSR) && (!defined(ENAMETOOLONG) || (ENAMETOOLONG != ENOSR))
+ case ENOSR: return "enosr";
+#endif
+#if defined(ENOSTR) && (!defined(ENOTTY) || (ENOTTY != ENOSTR))
+ case ENOSTR: return "enostr";
+#endif
+#ifdef ENOSYM
+ case ENOSYM: return "enosym";
+#endif
+#ifdef ENOSYS
+ case ENOSYS: return "enosys";
+#endif
+#ifdef ENOTBLK
+ case ENOTBLK: return "enotblk";
+#endif
+#ifdef ENOTCONN
+ case ENOTCONN: return "enotconn";
+#endif
+#ifdef ENOTDIR
+ case ENOTDIR: return "enotdir";
+#endif
+#if defined(ENOTEMPTY) && (!defined(EEXIST) || (ENOTEMPTY != EEXIST))
+ case ENOTEMPTY: return "enotempty";
+#endif
+#ifdef ENOTNAM
+ case ENOTNAM: return "enotnam";
+#endif
+#ifdef ENOTSOCK
+ case ENOTSOCK: return "enotsock";
+#endif
+#ifdef ENOTSUP
+ case ENOTSUP: return "enotsup";
+#endif
+#ifdef ENOTTY
+ case ENOTTY: return "enotty";
+#endif
+#ifdef ENOTUNIQ
+ case ENOTUNIQ: return "enotuniq";
+#endif
+#ifdef ENXIO
+ case ENXIO: return "enxio";
+#endif
+#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || EOPNOTSUPP != ENOTSUP)
+ case EOPNOTSUPP: return "eopnotsupp";
+#endif
+#ifdef EPERM
+ case EPERM: return "eperm";
+#endif
+#if defined(EPFNOSUPPORT) && (!defined(ENOLCK) || (ENOLCK != EPFNOSUPPORT))
+ case EPFNOSUPPORT: return "epfnosupport";
+#endif
+#ifdef EPIPE
+ case EPIPE: return "epipe";
+#endif
+#ifdef EPROCLIM
+ case EPROCLIM: return "eproclim";
+#endif
+#ifdef EPROCUNAVAIL
+ case EPROCUNAVAIL: return "eprocunavail";
+#endif
+#ifdef EPROGMISMATCH
+ case EPROGMISMATCH: return "eprogmismatch";
+#endif
+#ifdef EPROGUNAVAIL
+ case EPROGUNAVAIL: return "eprogunavail";
+#endif
+#ifdef EPROTO
+ case EPROTO: return "eproto";
+#endif
+#ifdef EPROTONOSUPPORT
+ case EPROTONOSUPPORT: return "eprotonosupport";
+#endif
+#ifdef EPROTOTYPE
+ case EPROTOTYPE: return "eprototype";
+#endif
+#ifdef ERANGE
+ case ERANGE: return "erange";
+#endif
+#if defined(EREFUSED) && (!defined(ECONNREFUSED) || (EREFUSED != ECONNREFUSED))
+ case EREFUSED: return "erefused";
+#endif
+#ifdef EREMCHG
+ case EREMCHG: return "eremchg";
+#endif
+#ifdef EREMDEV
+ case EREMDEV: return "eremdev";
+#endif
+#ifdef EREMOTE
+ case EREMOTE: return "eremote";
+#endif
+#ifdef EREMOTEIO
+ case EREMOTEIO: return "eremoteio";
+#endif
+#ifdef EREMOTERELEASE
+ case EREMOTERELEASE: return "eremoterelease";
+#endif
+#ifdef EROFS
+ case EROFS: return "erofs";
+#endif
+#ifdef ERPCMISMATCH
+ case ERPCMISMATCH: return "erpcmismatch";
+#endif
+#ifdef ERREMOTE
+ case ERREMOTE: return "erremote";
+#endif
+#ifdef ESHUTDOWN
+ case ESHUTDOWN: return "eshutdown";
+#endif
+#ifdef ESOCKTNOSUPPORT
+ case ESOCKTNOSUPPORT: return "esocktnosupport";
+#endif
+#ifdef ESPIPE
+ case ESPIPE: return "espipe";
+#endif
+#ifdef ESRCH
+ case ESRCH: return "esrch";
+#endif
+#ifdef ESRMNT
+ case ESRMNT: return "esrmnt";
+#endif
+#ifdef ESTALE
+ case ESTALE: return "estale";
+#endif
+#ifdef ESUCCESS
+ case ESUCCESS: return "esuccess";
+#endif
+#if defined(ETIME) && (!defined(ELOOP) || (ETIME != ELOOP))
+ case ETIME: return "etime";
+#endif
+#if defined(ETIMEDOUT) && (!defined(ENOSTR) || (ETIMEDOUT != ENOSTR))
+ case ETIMEDOUT: return "etimedout";
+#endif
+#ifdef ETOOMANYREFS
+ case ETOOMANYREFS: return "etoomanyrefs";
+#endif
+#ifdef ETXTBSY
+ case ETXTBSY: return "etxtbsy";
+#endif
+#ifdef EUCLEAN
+ case EUCLEAN: return "euclean";
+#endif
+#ifdef EUNATCH
+ case EUNATCH: return "eunatch";
+#endif
+#ifdef EUSERS
+ case EUSERS: return "eusers";
+#endif
+#ifdef EVERSION
+ case EVERSION: return "eversion";
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+ case EWOULDBLOCK: return "ewouldblock";
+#endif
+#ifdef EXDEV
+ case EXDEV: return "exdev";
+#endif
+#ifdef EXFULL
+ case EXFULL: return "exfull";
+#endif
+#ifdef WSAEINTR
+ case WSAEINTR: return "eintr";
+#endif
+#ifdef WSAEBADF
+ case WSAEBADF: return "ebadf";
+#endif
+#ifdef WSAEACCES
+ case WSAEACCES: return "eacces";
+#endif
+#ifdef WSAEFAULT
+ case WSAEFAULT: return "efault";
+#endif
+#ifdef WSAEINVAL
+ case WSAEINVAL: return "einval";
+#endif
+#ifdef WSAEMFILE
+ case WSAEMFILE: return "emfile";
+#endif
+#ifdef WSAEWOULDBLOCK
+ case WSAEWOULDBLOCK: return "ewouldblock";
+#endif
+#ifdef WSAEINPROGRESS
+ case WSAEINPROGRESS: return "einprogress";
+#endif
+#ifdef WSAEALREADY
+ case WSAEALREADY: return "ealready";
+#endif
+#ifdef WSAENOTSOCK
+ case WSAENOTSOCK: return "enotsock";
+#endif
+#ifdef WSAEDESTADDRREQ
+ case WSAEDESTADDRREQ: return "edestaddrreq";
+#endif
+#ifdef WSAEMSGSIZE
+ case WSAEMSGSIZE: return "emsgsize";
+#endif
+#ifdef WSAEPROTOTYPE
+ case WSAEPROTOTYPE: return "eprototype";
+#endif
+#ifdef WSAENOPROTOOPT
+ case WSAENOPROTOOPT: return "enoprotoopt";
+#endif
+#ifdef WSAEPROTONOSUPPORT
+ case WSAEPROTONOSUPPORT: return "eprotonosupport";
+#endif
+#ifdef WSAESOCKTNOSUPPORT
+ case WSAESOCKTNOSUPPORT: return "esocktnosupport";
+#endif
+#ifdef WSAEOPNOTSUPP
+ case WSAEOPNOTSUPP: return "eopnotsupp";
+#endif
+#ifdef WSAEPFNOSUPPORT
+ case WSAEPFNOSUPPORT: return "epfnosupport";
+#endif
+#ifdef WSAEAFNOSUPPORT
+ case WSAEAFNOSUPPORT: return "eafnosupport";
+#endif
+#ifdef WSAEADDRINUSE
+ case WSAEADDRINUSE: return "eaddrinuse";
+#endif
+#ifdef WSAEADDRNOTAVAIL
+ case WSAEADDRNOTAVAIL: return "eaddrnotavail";
+#endif
+#ifdef WSAENETDOWN
+ case WSAENETDOWN: return "enetdown";
+#endif
+#ifdef WSAENETUNREACH
+ case WSAENETUNREACH: return "enetunreach";
+#endif
+#ifdef WSAENETRESET
+ case WSAENETRESET: return "enetreset";
+#endif
+#ifdef WSAECONNABORTED
+ case WSAECONNABORTED: return "econnaborted";
+#endif
+#ifdef WSAECONNRESET
+ case WSAECONNRESET: return "econnreset";
+#endif
+#ifdef WSAENOBUFS
+ case WSAENOBUFS: return "enobufs";
+#endif
+#ifdef WSAEISCONN
+ case WSAEISCONN: return "eisconn";
+#endif
+#ifdef WSAENOTCONN
+ case WSAENOTCONN: return "enotconn";
+#endif
+#ifdef WSAESHUTDOWN
+ case WSAESHUTDOWN: return "eshutdown";
+#endif
+#ifdef WSAETOOMANYREFS
+ case WSAETOOMANYREFS: return "etoomanyrefs";
+#endif
+#ifdef WSAETIMEDOUT
+ case WSAETIMEDOUT: return "etimedout";
+#endif
+#ifdef WSAECONNREFUSED
+ case WSAECONNREFUSED: return "econnrefused";
+#endif
+#ifdef WSAELOOP
+ case WSAELOOP: return "eloop";
+#endif
+#ifdef WSAENAMETOOLONG
+ case WSAENAMETOOLONG: return "enametoolong";
+#endif
+#ifdef WSAEHOSTDOWN
+ case WSAEHOSTDOWN: return "ehostdown";
+#endif
+#ifdef WSAEHOSTUNREACH
+ case WSAEHOSTUNREACH: return "ehostunreach";
+#endif
+#ifdef WSAENOTEMPTY
+ case WSAENOTEMPTY: return "enotempty";
+#endif
+#ifdef WSAEPROCLIM
+ case WSAEPROCLIM: return "eproclim";
+#endif
+#ifdef WSAEUSERS
+ case WSAEUSERS: return "eusers";
+#endif
+#ifdef WSAEDQUOT
+ case WSAEDQUOT: return "edquot";
+#endif
+#ifdef WSAESTALE
+ case WSAESTALE: return "estale";
+#endif
+#ifdef WSAEREMOTE
+ case WSAEREMOTE: return "eremote";
+#endif
+#ifdef WSASYSNOTREADY
+ case WSASYSNOTREADY: return "sysnotready";
+#endif
+#ifdef WSAVERNOTSUPPORTED
+ case WSAVERNOTSUPPORTED: return "vernotsupported";
+#endif
+#ifdef WSANOTINITIALISED
+ case WSANOTINITIALISED: return "notinitialised";
+#endif
+#ifdef WSAEDISCON
+ case WSAEDISCON: return "ediscon";
+#endif
+#ifdef WSAENOMORE
+ case WSAENOMORE: return "enomore";
+#endif
+#ifdef WSAECANCELLED
+ case WSAECANCELLED: return "ecancelled";
+#endif
+#ifdef WSAEINVALIDPROCTABLE
+ case WSAEINVALIDPROCTABLE: return "einvalidproctable";
+#endif
+#ifdef WSAEINVALIDPROVIDER
+ case WSAEINVALIDPROVIDER: return "einvalidprovider";
+#endif
+#ifdef WSAEPROVIDERFAILEDINIT
+ case WSAEPROVIDERFAILEDINIT: return "eproviderfailedinit";
+#endif
+#ifdef WSASYSCALLFAILURE
+ case WSASYSCALLFAILURE: return "syscallfailure";
+#endif
+#ifdef WSASERVICE_NOT_FOUND
+ case WSASERVICE_NOT_FOUND: return "service_not_found";
+#endif
+#ifdef WSATYPE_NOT_FOUND
+ case WSATYPE_NOT_FOUND: return "type_not_found";
+#endif
+#ifdef WSA_E_NO_MORE
+ case WSA_E_NO_MORE: return "e_no_more";
+#endif
+#ifdef WSA_E_CANCELLED
+ case WSA_E_CANCELLED: return "e_cancelled";
+#endif
+ default:
+ sprintf(errstrbuf, "unknown:%d", error);
+ return errstrbuf;
+ }
+}
+
diff --git a/lib/ssl/c_src/esock_posix_str.h b/lib/ssl/c_src/esock_posix_str.h
new file mode 100644
index 0000000000..53916c888a
--- /dev/null
+++ b/lib/ssl/c_src/esock_posix_str.h
@@ -0,0 +1,28 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1999-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%
+ */
+
+/* esock_posix_str.h */
+
+#ifndef ESOCK_POSIX_STR_H
+#define ESOCK_POSIX_STR_H
+
+char *esock_posix_str(int error);
+
+#endif
+
diff --git a/lib/ssl/c_src/esock_ssl.h b/lib/ssl/c_src/esock_ssl.h
new file mode 100644
index 0000000000..535e9a6491
--- /dev/null
+++ b/lib/ssl/c_src/esock_ssl.h
@@ -0,0 +1,110 @@
+/*<copyright>
+ * <year>1999-2008</year>
+ * <holder>Ericsson AB, All Rights Reserved</holder>
+ *</copyright>
+ *<legalnotice>
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson AB.
+ *</legalnotice>
+ */
+/*
+ * Purpose: Header file for adaptions to various SSL packages.
+ */
+
+#ifndef ESOCK_SSL_H
+#define ESOCK_SSL_H
+
+#include <sys/types.h>
+#include <stdio.h>
+#include "esock.h"
+
+typedef struct {
+ const char *compile_version;/* version of OpenSSL when compiling esock */
+ const char *lib_version; /* version of OpenSSL in library */
+} esock_version;
+
+/* Variables to be set by certain functions (see below) */
+char *esock_ssl_errstr;
+
+/* Ephemeral RSA and DH */
+int ephemeral_rsa, ephemeral_dh;
+
+/* Protocol version (sslv2, sslv3, tlsv1) */
+int protocol_version;
+
+/* version info */
+esock_version *esock_ssl_version(void);
+
+/* ciphers info */
+char *esock_ssl_ciphers(void);
+
+/* seeding */
+void esock_ssl_seed(void *buf, int len);
+
+/* Initialization and finalization of SSL */
+
+int esock_ssl_init(void);
+void esock_ssl_finish(void);
+
+/* Freeing of SSL resources for a connection */
+
+void esock_ssl_free(Connection *cp);
+
+/* Print error diagnostics to a file pointer */
+
+void esock_ssl_print_errors_fp(FILE *fp);
+
+/* All functions below have to return >= 0 on success, and < 0 on
+ * failure.
+ *
+ * If the return indicates a failure (return value < 0) and the failure
+ * is temporary the error context (sock_errno()/sock_set_errno()) must
+ * be set to ERRNO_BLOCK.
+ *
+ * If the failure is permanent, the error context must be set to something
+ * else than ERRNO_BLOCK, and `esock_ssl_errstr' must be set to point to
+ * short diagnostic string describing the error.
+ */
+
+int esock_ssl_accept_init(Connection *cp, void *listenssl);
+int esock_ssl_connect_init(Connection *cp);
+int esock_ssl_listen_init(Connection *cp);
+
+/* All functions below may involve non-blocking I/O with a temporary
+ * failure. Hence they have to have the error context set to
+ * ERRNO_BLOCK, or else have esock_ssl_errstr set to point to a
+ * diagnostic string, in case the return value is < 0. If the return
+ * value is 0, cp->eof and cp->bp are set, if appropritate.
+ */
+
+int esock_ssl_accept(Connection *cp);
+int esock_ssl_connect(Connection *cp);
+
+int esock_ssl_read(Connection *cp, char *buf, int len);
+int esock_ssl_write(Connection *cp, char *buf, int len);
+
+int esock_ssl_shutdown(Connection *cp);
+
+/* Peer certificate */
+
+int esock_ssl_getpeercert(Connection *cp, unsigned char **buf);
+int esock_ssl_getpeercertchain(Connection *cp, unsigned char **buf);
+
+/* Sessions */
+int esock_ssl_session_reused(Connection *cp);
+
+/* Protocol version and cipher of established connection */
+int esock_ssl_getprotocol_version(Connection *cp, char **buf);
+int esock_ssl_getcipher(Connection *cp, char **buf);
+
+#endif
diff --git a/lib/ssl/c_src/esock_utils.c b/lib/ssl/c_src/esock_utils.c
new file mode 100644
index 0000000000..0098a4f5f6
--- /dev/null
+++ b/lib/ssl/c_src/esock_utils.c
@@ -0,0 +1,150 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1999-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: Safe memory allocation and other utilities.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "esock_utils.h"
+
+static char *strtok_quote(char *s1, const char *s2);
+
+
+void *esock_malloc(size_t size)
+{
+ void *p;
+
+ p = malloc(size);
+ if (!p) {
+ fprintf(stderr, "esock_malloc: cannot alloc %d bytes\n", size);
+ exit(EXIT_FAILURE);
+ }
+ return p;
+}
+
+void *esock_realloc(void *p, size_t size)
+{
+ void *np;
+
+ np = realloc(p, size);
+ if (!np) {
+ fprintf(stderr, "esock_realloc: cannot realloc %d bytes\n", size);
+ exit(EXIT_FAILURE);
+ }
+ return np;
+}
+
+void esock_free(void *p)
+{
+ free(p);
+}
+
+/* Builds an argv array from cmd. Spaces and tabs within double quotes
+ * are not considered delimiters. Double quotes are removed.
+ *
+ * The return value is argc, and the pointer to char ** is set. argc
+ * is non-negative, argv[0], ..., argv[argc - 1] are pointers to
+ * strings, and argv[argc] == NULL. All argv[0], ..., argv[argc - 1]
+ * must be freed by the user, and also the argv pointer itself.
+ *
+ * Example: cmd = abc"/program files/"olle nisse, results in
+ * argv[0] = abc/program files/olle, argv[1] = nisse, argc = 2.
+ *
+ */
+int esock_build_argv(char *cmd, char ***argvp)
+{
+ int argvsize = 10, argc = 0;
+ char *args, *tokp, *argp;
+ char **argv;
+
+ argv = esock_malloc(argvsize * sizeof(char *));
+ args = esock_malloc(strlen(cmd) + 1);
+ strcpy(args, cmd);
+ tokp = strtok_quote(args, " \t");
+ while (tokp != NULL) {
+ if (argc + 1 >= argvsize) {
+ argvsize += 10;
+ argv = esock_realloc(argv, argvsize * sizeof(char *));
+ }
+ argp = esock_malloc(strlen(tokp) + 1);
+ strcpy(argp, tokp);
+ argv[argc++] = argp;
+ tokp = strtok_quote(NULL, " \t");
+ }
+ esock_free(args);
+ argv[argc] = NULL;
+ *argvp = argv;
+ return argc;
+}
+
+/* strtok_quote
+ * Works as strtok, but characters within pairs of double quotes are not
+ * considered as delimiters. Quotes are removed.
+ */
+static char *strtok_quote(char *s1, const char *s2)
+{
+ static char *last;
+ char *s, *t, *u;
+
+ s = (s1) ? s1 : last;
+ if (!s)
+ return last = NULL;
+
+ while (*s != '"' && *s != '\0' && strchr(s2, *s))
+ s++;
+ t = s;
+
+ while (1) {
+ if (*t == '"') {
+ t++;
+ while (*t != '"' && *t != '\0')
+ t++;
+ if (*t == '\0') {
+ last = NULL;
+ goto end;
+ }
+ t++;
+ }
+ while(*t != '"' && *t != '\0' && !strchr(s2, *t))
+ t++;
+ if (*t == '\0') {
+ last = NULL;
+ goto end;
+ } else if (*t != '"') {
+ *t = '\0';
+ last = t + 1;
+ goto end;
+ }
+ }
+end:
+ /* Remove quotes */
+ u = t = s;
+ while (*u) {
+ if (*u == '"')
+ u++;
+ else
+ *t++ = *u++;
+ }
+ *t = '\0';
+ return s;
+}
+
diff --git a/lib/ssl/c_src/esock_utils.h b/lib/ssl/c_src/esock_utils.h
new file mode 100644
index 0000000000..99ed6c23e3
--- /dev/null
+++ b/lib/ssl/c_src/esock_utils.h
@@ -0,0 +1,32 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1999-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 ESOCK_UTILS_H
+#define ESOCK_UTILS_H
+
+#include <stdlib.h>
+
+void *esock_malloc(size_t size);
+void *esock_realloc(void *p, size_t size);
+void esock_free(void *p);
+int esock_build_argv(char *cmd, char ***argvp);
+
+#endif
+
+
diff --git a/lib/ssl/c_src/esock_winsock.h b/lib/ssl/c_src/esock_winsock.h
new file mode 100644
index 0000000000..069782a18d
--- /dev/null
+++ b/lib/ssl/c_src/esock_winsock.h
@@ -0,0 +1,36 @@
+/*<copyright>
+ * <year>2003-2008</year>
+ * <holder>Ericsson AB, All Rights Reserved</holder>
+ *</copyright>
+ *<legalnotice>
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson AB.
+ *</legalnotice>
+ */
+/*
+ * Purpose: Control winsock version and setting of FD_SETSIZE.
+ *
+ */
+
+/* Maybe set FD_SETSIZE */
+
+#ifdef ESOCK_WINSOCK2
+#include <winsock2.h>
+#else
+#include <winsock.h>
+/* These are defined in winsock2.h but not in winsock.h */
+#define SD_RECEIVE 0x00
+#define SD_SEND 0x01
+#define SD_BOTH 0x02
+#endif
+
diff --git a/lib/ssl/doc/html/.gitignore b/lib/ssl/doc/html/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ssl/doc/html/.gitignore
diff --git a/lib/ssl/doc/man3/.gitignore b/lib/ssl/doc/man3/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ssl/doc/man3/.gitignore
diff --git a/lib/ssl/doc/man6/.gitignore b/lib/ssl/doc/man6/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ssl/doc/man6/.gitignore
diff --git a/lib/ssl/doc/pdf/.gitignore b/lib/ssl/doc/pdf/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ssl/doc/pdf/.gitignore
diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile
new file mode 100644
index 0000000000..54d274ef83
--- /dev/null
+++ b/lib/ssl/doc/src/Makefile
@@ -0,0 +1,130 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-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)
+APPLICATION=ssl
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+XML_APPLICATION_FILES = refman.xml
+XML_REF3_FILES = ssl.xml new_ssl.xml
+XML_REF6_FILES = ssl_app.xml
+
+XML_PART_FILES = release_notes.xml usersguide.xml
+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
+
+GIF_FILES = warning.gif
+
+PS_FILES =
+
+XML_FLAGS += -defs cite cite.defs -booksty otpA4
+
+# ----------------------------------------------------
+
+HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+
+INFO_FILE = ../../info
+EXTRA_FILES = \
+ $(DEFAULT_GIF_FILES) \
+ $(DEFAULT_HTML_FILES) \
+ $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_REF6_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
+
+MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
+MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
+
+HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
+
+TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+XML_FLAGS +=
+DVIPS_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+$(HTMLDIR)/%.gif: %.gif
+ $(INSTALL_DATA) $< $@
+
+docs: pdf html man
+
+$(TOP_PDF_FILE): $(XML_FILES)
+
+pdf: $(TOP_PDF_FILE)
+
+html: gifs $(HTML_REF_MAN_FILE)
+
+clean clean_docs:
+ rm -rf $(HTMLDIR)/*
+ rm -f $(MAN3DIR)/*
+ rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f errs core *~
+
+man: $(MAN3_FILES) $(MAN6_FILES)
+
+gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+
+debug opt:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_docs_spec: docs
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(HTMLDIR)/* \
+ $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
+ $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man6
+ $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6
+
+release_spec:
diff --git a/lib/ssl/doc/src/book.xml b/lib/ssl/doc/src/book.xml
new file mode 100644
index 0000000000..9122addb74
--- /dev/null
+++ b/lib/ssl/doc/src/book.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE book SYSTEM "book.dtd">
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header titlestyle="normal">
+ <copyright>
+ <year>1999</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>Secure Socket Layer </title>
+ <prepared>Peter H&ouml;gfeldt</prepared>
+ <docno></docno>
+ <date>1999-01-25</date>
+ <rev>A</rev>
+ <file>book.sgml</file>
+ </header>
+ <insidecover>
+ <include file="insidecover"></include>
+ </insidecover>
+ <pagetext>SSL Application</pagetext>
+ <preamble>
+ <contents level="2"></contents>
+ </preamble>
+ <parts lift="yes">
+ <xi:include href="usersguide.xml"/>
+ </parts>
+ <applications>
+ <xi:include href="refman.xml"/>
+ </applications>
+ <releasenotes>
+ <xi:include href="notes.xml"/>
+ </releasenotes>
+ <listofterms></listofterms>
+ <index></index>
+</book>
+
+
diff --git a/lib/ssl/doc/src/cite.defs b/lib/ssl/doc/src/cite.defs
new file mode 100644
index 0000000000..45c9054e32
--- /dev/null
+++ b/lib/ssl/doc/src/cite.defs
@@ -0,0 +1,112 @@
+[
+{"4711","CCITT","CCITT, Red Book Vol.-FASCILE VIII.7, Recomm. X400-X.430, Geneva, 1985","jocke"},
+{"X.680","ITU-T X.680","ITU-T Recommendation X.680 (1994) | ISO/IEC 8824-1: 1995, Abstract Syntax Notation One (ASN.1): Specification of Basic Notation"},
+{"X.682","ITU-T X.682","ITU-T Recommendation X.682 (1994) | ISO/IEC 8824-3: 1995, Abstract Syntax Notation One (ASN.1): Constraint Specification"},
+{"X.690","ITU-T X.690","ITU-T Recommendation X.690 (1994) | ISO/IEC 8825-1: 1995, ASN.1 Encoding Rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)"},
+{"X.691","ITU-T X.691","ITU-T Recommendation X.691 (04/95) | ISO/IEC 8825-2: 1995, ASN.1 Encoding Rules: Specification of Packed Encoding Rules (PER)"},
+{"X.681","ITU-T X.681","ITU-T Recommendation X.681 (1994) | ISO/IEC 8824-2: 1995, Abstract Syntax Notation One (ASN.1): Information Object Specification"},
+{"X.683","ITU-T X.683","ITU-T Recommendation X.683 (1994) | ISO/IEC 8824-4: 1995, Abstract Syntax Notation One (ASN.1): Parameterization of ASN.1 Specifications"},
+{"X.509","ITU-T X.509","ITU-T Recommendation X.509 (1997) | "
+ "ISO/IEC 9594-8:1997 : "
+ "http://www.itu.int/ITU-T/asn1/database/itu-t/x/x509/1997/index.html",
+ peter},
+{
+ "DUBUISSON",
+ "ASN.1 Communication between Heterogeneous Systems",
+ "Oliver Dubuisson: ASN.1 Communication between Heterogeneous Systems, "
+ "June 2000 ISBN 0-126333361-0", "nibe"
+ },
+ {
+ "erlbook2",
+ "Concurrent Programming in ERLANG",
+ "J. Armstrong, R. Virding, C. Wikstrom, M. Williams: "
+ "Concurrent Programming in ERLANG, Prentice Hall, 1996, ISBN 0-13-508301-X",
+ "kent"
+ },
+ {"practsgml",
+ "Practical SGML",
+ "Eric van Herwijnen: Practical SGML, Kluwer Academic Publishers, 1990.",
+ "peter"
+ },
+ {"ssl",
+ "SSL",
+ "The SSL Protocol Version 3.0, Internet draft November 18, 1996."
+ "peter"
+ },
+ {"rescorla",
+ "SSL and TLS",
+ "Eric Rescorla: SSL and TLS - Designing and Building Secure Systems, "
+ "Addison-Wesley, 2001, ISBN 0-201-61598-3.",
+ "peter"
+ },
+ {"rfc3279",
+ "RFC 3279",
+ "Algorithms and Identifiers for the Internet X.509 Public Key "
+ "Infrastructure Certificate and Certificate Revocation List (CRL) Profile.",
+ "peter"
+ },
+ {"rfc3280",
+ "RFC 3280",
+ "Internet X.509 Public Key Infrastructure Certificate and "
+ "Certificate Revocation List (CRL) Profile.",
+ "peter"
+ },
+ {"rfc3281",
+ "RFC 3281",
+ "An Internet Attribute Certificate Profile for Authorization.",
+ "peter"
+ },
+ {"pkcs1",
+ "PKCS #1",
+ "[RFC 2437] RSA Cryptography Specifications, version 2.0 .",
+ "peter"
+ },
+ {"pkcs3",
+ "PKCS #3",
+ "Diffie-Hellman Key-Agreement Standard, version 1.4, November 1993.",
+ "peter"
+ },
+ {"pkcs5",
+ "PKCS #5",
+ "[RFC 2898] Password-Based Cryptography Specification, version 2.0 .",
+ "peter"
+ },
+ {"pkcs6",
+ "PKCS #6",
+ "Extended-Certificate Syntax Standard, version 1.5, November 1993.",
+ "peter"
+ },
+ {"pkcs7",
+ "PKCS #7",
+ "Cryptographic Message Syntax Standard, version 1.5, November 1993.",
+ "peter"
+ },
+ {"pkcs8",
+ "PKCS #8",
+ "Private-Key Information Syntax Standard, version 1.2, November 1993.",
+ "peter"
+ },
+ {"pkcs9",
+ "PKCS #9",
+ "[RFC 2985] Selected Object Classes and Attribute Types, "
+ "version 2.0 .",
+ "peter"
+ },
+ {"pkcs10",
+ "PKCS #10",
+ "[RFC 2986] Certification Request Syntax Specification, version 1.7 .",
+ "peter"
+ },
+ {"pkcs12",
+ "PKCS #12",
+ "Personal Information Exchange Syntax Standard, version 1.0 DRAFT, "
+ "30 April 1997.",
+ "peter"
+ },
+ {"schneier",
+ "Applied Cryptography",
+ "Bruce Schneier: Applied Cryptography: Protocols, Algorithms, and Source Code in C, "
+ "Second Edition, John Wiley & Sons, 1995, ISBN 0471117099",
+ "peter"
+ }
+].
diff --git a/lib/ssl/doc/src/create_certs.xml b/lib/ssl/doc/src/create_certs.xml
new file mode 100644
index 0000000000..15958ee457
--- /dev/null
+++ b/lib/ssl/doc/src/create_certs.xml
@@ -0,0 +1,148 @@
+<?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&ouml;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">
+\011openssl req -new -x509 -config /some/path/req.cnf \\
+\011 -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">
+\011openssl req -new -config /some/path/req.cnf \\
+\011 -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">
+\011openssl ca -batch -notext -config /some/path/req.cnf \\
+\011 -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/fascicules.xml b/lib/ssl/doc/src/fascicules.xml
new file mode 100644
index 0000000000..7ee764fda3
--- /dev/null
+++ b/lib/ssl/doc/src/fascicules.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
+
+<fascicules>
+ <fascicule file="usersguide" href="usersguide_frame.html" entry="no">
+ User's Guide
+ </fascicule>
+ <fascicule file="refman" href="refman_frame.html" entry="yes">
+ Reference Manual
+ </fascicule>
+ <fascicule file="release_notes" href="release_notes_frame.html" entry="no">
+ Release Notes
+ </fascicule>
+ <fascicule file="" href="../../../../doc/print.html" entry="no">
+ Off-Print
+ </fascicule>
+</fascicules>
+
+
diff --git a/lib/ssl/doc/src/insidecover.xml b/lib/ssl/doc/src/insidecover.xml
new file mode 100644
index 0000000000..4f3f5e5951
--- /dev/null
+++ b/lib/ssl/doc/src/insidecover.xml
@@ -0,0 +1,14 @@
+<?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
new file mode 100644
index 0000000000..0969f9ad6e
--- /dev/null
+++ b/lib/ssl/doc/src/licenses.xml
@@ -0,0 +1,156 @@
+<?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&ouml;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
+ *
+ * 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
new file mode 100644
index 0000000000..2ff81bee1f
--- /dev/null
+++ b/lib/ssl/doc/src/make.dep
@@ -0,0 +1,30 @@
+# ----------------------------------------------------
+# >>>> 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
new file mode 100644
index 0000000000..f50f714fe6
--- /dev/null
+++ b/lib/ssl/doc/src/new_ssl.xml
@@ -0,0 +1,678 @@
+<?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. The alfa version has a
+ few limitations that will be removed before the ssl-4.0
+ release. Main differences and limitations in the alfa 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>Diffie-Hellman keyexchange is
+ not supported yet.</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()} | {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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CallbackModule =
+ atom()</c>
+ </p> <p><c>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataTag =
+ atom() - tag used in socket data message.</c></p>
+ <p><c>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 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>{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) -> </name>
+ <name>peercert(Socket, Opts) -> {ok, Cert} | {error, Reason}</name>
+ <fsummary>Return the peer certificate.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Opts = [] | [otp] | [plain] </v>
+ <v>Cert = term()</v>
+ <v>Subject = term()</v>
+ </type>
+ <desc>
+ <p><c>peercert(Cert)</c> is equivalent to <c>peercert(Cert, [])</c>.
+ </p>
+ <p>The form of the returned certificate depends on the
+ options.
+ </p>
+ <p>If the options list is empty the certificate is returned as
+ a DER encoded binary.
+ </p>
+ <p>The option <c>otp</c> or <c>plain</c> implies that the
+ certificate will be returned as a parsed ASN.1 structure in the
+ form of an Erlang term. For detail see the public_key application.
+ Currently only plain is officially supported see the public_key users
+ guide.
+ </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> &gt; 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>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/note.gif b/lib/ssl/doc/src/note.gif
new file mode 100644
index 0000000000..6fffe30419
--- /dev/null
+++ b/lib/ssl/doc/src/note.gif
Binary files differ
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
new file mode 100644
index 0000000000..14dfe616ba
--- /dev/null
+++ b/lib/ssl/doc/src/notes.xml
@@ -0,0 +1,1132 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>1999</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>SSL Release Notes</title>
+ <prepared>Peter H&ouml;gfeldt</prepared>
+ <docno></docno>
+ <date>2003-08-03</date>
+ <rev>G</rev>
+ <file>notes.xml</file>
+ </header>
+ <p>This document describes the changes made to the SSL application.
+ </p>
+
+<section><title>SSL 3.10.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A ticker process could potentially be blocked
+ indefinitely trying to send a tick to a node not
+ responding. If this happened, the connection would not be
+ brought down as it should.</p>
+ <p> This requires erts-5.7.4 and kernel-2.13.4 or later
+ to be able to get the erlang distribution over ssl to work.</p>
+ <p>
+ Own Id: OTP-8218</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The documentation is now built with open source tools
+ (xsltproc and fop) that exists on most platforms. One
+ visible change is that the frames are removed.</p>
+ <p>
+ Own Id: OTP-8250</p>
+ </item>
+ <item>
+ <p>
+ Code cleanup from Kostis.</p>
+ <p>
+ Own Id: OTP-8260</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 3.10.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The ssl:ssl_accept/3 issue was not properly fixed in the
+ previous patch, see OTP-8244.</p>
+ <p>
+ Own Id: OTP-8275 Aux Id: seq11451 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 3.10.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Allow clients to not send certificates if option
+ <c>fail_if_no_peer_cert</c> was not set.</p>
+ <p>
+ Own Id: OTP-8224</p>
+ </item>
+ <item>
+ <p>A ssl:ssl_accept/3 could crash a connection if the
+ timing was wrong.</p> <p>Removed info message if the
+ socket closed without a proper disconnect from the ssl
+ layer. </p> <p>ssl:send/2 is now blocking until the
+ message is sent.</p>
+ <p>
+ Own Id: OTP-8244 Aux Id: seq11420 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 3.10.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A client could avoid a certificate check if the client
+ code didn't send the requested certificate.</p>
+ <p>
+ Own Id: OTP-8137</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 3.10.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Packet handling was not implemented correctly.</p>
+ <p>Inet option handling support have been improved.</p>
+ <p>The <c>verify_fun</c> is now invoked even if
+ verify_peer is used, that implies that by default
+ {bad_cert,unknown_ca} is an accepted fault during the
+ client connection phase. The check can still be done by
+ suppling another verify_fun.</p>
+ <p>
+ Own Id: OTP-8011 Aux Id: seq11287 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+
+<section><title>SSL 3.10.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A "new_ssl" socket was not closed if the controlling
+ process died without calling ssl:close/1.</p>
+ <p>
+ Own Id: OTP-7963 Aux Id: seq11276 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 3.10.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed bug that caused the ssl handshake finished message
+ to be calculated wrongly under the circumstances that the
+ server did not send the trusted cert and that the
+ previous cert did not have the extension telling us the
+ trusted certs name. This manifested it self as
+ bad_record_mac alert from the server.</p>
+ <p>
+ Own Id: OTP-7878</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The cacertsfile option is now optional for ssl servers.</p>
+ <p>
+ Own Id: OTP-7656</p>
+ </item>
+ <item>
+ <p>
+ For the ssl client the options cacertfile, certfile and
+ keyfile are now optional as they are not always needed
+ depending on configuration of the client itself and the
+ configuration of the server. Also as PEM-files may
+ contain more than one entry the keyfile option will
+ default to the same file as given by the certfile option.</p>
+ <p>
+ Own Id: OTP-7870</p>
+ </item>
+ <item>
+ <p>
+ Added new ssl client option verify_fun.</p>
+ <p>
+ Own Id: OTP-7871</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+ <section><title>SSL 3.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Error log entries are now formatted correctly.</p>
+ <p>
+ Own Id: OTP-7258</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ All handling of X509-certificates and public keys have
+ been moved to the new application public_key.</p>
+ <p>
+ Own Id: OTP-6894</p>
+ </item>
+ <item>
+ <p>
+ New ssl now supports SSL-3.0 and TLS-1.0</p>
+ <p>
+ Own Id: OTP-7037</p>
+ </item>
+ <item>
+ <p>
+ New ssl now supports all inet-packet types.</p>
+ <p>
+ Own Id: OTP-7039</p>
+ </item>
+ <item>
+ <p>
+ The new ssl-server is now able to send a certificate
+ request to the client. However new options may be
+ introduced later to fully support all features regarding
+ certificate requests.</p>
+ <p>
+ Own Id: OTP-7150</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ Running erlang distribution over ssl don't work as
+ described in the documentation.</p>
+ <p>
+ Own Id: OTP-7536</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+
+
+ <section><title>SSL 3.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ ssl_prim.erl was passing an FD rather than an #sslsocket
+ to ssl_broker:ssl_accept_prim. This could cause problems
+ in the deprecated accept function, this will not cause
+ any more problems however this function is deprecated!</p>
+ <p>
+ Own Id: OTP-6926</p>
+ </item>
+ <item>
+ <p>
+ Erlang distribution over ssl was broken after R11B-0,
+ this has now been fixed.</p>
+ <p>
+ Own Id: OTP-7004</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ All inet options are available in the new ssl
+ implementation that is released as a alfa in ssl-3.9 and
+ will replace the old implementation in ssl-4.0. This will
+ not be fixed in the old implementation.</p>
+ <p>
+ Own Id: OTP-4677</p>
+ </item>
+ <item>
+ <p>
+ The new ssl implementation released as a alfa in this
+ version supports upgrading of a tcp connection to a ssl
+ connection so that http client and servers may implement
+ RFC 2817.</p>
+ <p>
+ Own Id: OTP-5510</p>
+ </item>
+ <item>
+ <p>A new implementation of ssl is released as a alfa
+ version in ssl-3.9 it will later replace the old
+ implementation in ssl-4.0. 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. The alfa version has a
+ few limitations that will be removed before the ssl-4.0
+ release. Main differences and limitations in the alfa 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>Diffie-Hellman keyexchange is
+ not supported.</item> <item>Not all inet packet types are
+ supported.</item> <item>CRL and policy certificate
+ extensions are not supported.</item> <item>In this alfa
+ only sslv3 is enabled, although tlsv1 and tlsv1.1
+ versions are implemented and will be supported in future
+ versions.</item> <item>For security reasons sslv2 is not
+ supported.</item> </list>
+ <p>
+ Own Id: OTP-6619</p>
+ </item>
+ <item>
+ <p>
+ New ssl implementation, released as alfa in ssl-3.9,
+ supports ipv6. It will not be supported in the old
+ implementation.</p>
+ <p>
+ Own Id: OTP-6637 Aux Id: OTP-6636 </p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+
+ <section>
+ <title>SSL 3.1.1.1</title>
+
+ <section>
+ <title>Minor Makefile changes</title>
+ <list type="bulleted">
+ <item>
+ <p>Removed use of <c>erl_flags</c> from Makefile.</p>
+ <p>Own Id: OTP-6689</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 3.1.1</title>
+
+ <section>
+ <title>Crash on error in ssl_accept</title>
+ <list type="bulleted">
+ <item>
+ <p>A bug in ssl_accept could cause all ssl
+ connections to hang when a connection
+ attempt was closed by the client while
+ the server was in <c>ssl_accept</c>.</p>
+ <p>Own Id: OTP-6612 Aux Id: seq10599</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 3.1</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>SSL now uses a two-phase accept, with a separate accept
+ calls for the socket and the ssl protocol. This avoids
+ timeouts when a client doesn't initiate ssl handshake.</p>
+ <p>With the old implementation of accept, the server
+ was locked by a client, if the client didn't do
+ proper ssl handshake.</p>
+ <p>Own Id: OTP-6418 Aux Id: seq10105</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 3.0.12</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>An integer array pointing to a struct pollfd array, is
+ now reset before file descriptors are collected to be
+ included in a call to poll(). This is to prevent file
+ descriptors to be mixed up.</p>
+ <p>Own Id: OTP-6084</p>
+ </item>
+ <item>
+ <p>The generation of the module ssl_pkix_oid contained
+ multiple identifiers, which made the mapping between
+ atoms and identifiers not one-to-one.</p>
+ <p>Own Id: OTP-6085</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 3.0.11</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>The state of a connection in active mode could be in a
+ restrictive state, so that an internal tcp_closed message
+ was incorrectly considered illegal, resulting in a
+ premature termination of the connection process.</p>
+ <p>Own Id: OTP-5972 Aux Id: seq10188 </p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 3.0.10</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Erlang distribution over SSL was broken. Corrected.
+ (Thanks to Fredrik Thulin.)</p>
+ <p>Own Id: OTP-5863</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 3.0.9</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>The port program for the ssl application could waste huge
+ amounts of CPU time if a write could not be completed
+ directly and was put in the write queue. (Only on platforms
+ where poll() is used, such as Solaris and Linux.)</p>
+ <p>Own Id: OTP-5784</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 3.0.8</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>A process reading only a portion of a sufficiently large
+ amount of data from an accepted socket, and then quering
+ the ssl library (e.g. ssl:getpeername()), would cause a
+ global deadlock in the esock port program.</p>
+ <p>Own Id: OTP-5702</p>
+ </item>
+ <item>
+ <p>A spelling error in the module <c>ssl_pkix</c> caused the
+ call to <c>ssl:peercert/2</c> to fail when the option
+ <c>subject</c> was used.</p>
+ <p>Own Id: OTP-5708</p>
+ </item>
+ <item>
+ <p>Because fopen() on Solaris 8 can't handle file
+ descriptor numbers above 255, reading of certificate
+ files would fail if all file descriptors below 256 were
+ in use (typically, if many connections were open). This
+ problem has been worked around.</p>
+ <p>The ssl application's port program used to use
+ select(), which meant that it could not handle more than
+ FD_SETSIZE file descriptors (usually 1024). To eliminate
+ that limitation, poll() is now used on all platforms that
+ support it.</p>
+ <p>Solaris/Sparc, 64-bit emulator: The SO_REUSEADDR
+ option was not set for listen sockets, which essentially
+ made the ssl application unusable. Corrected.</p>
+ <p>The default listen queue size for ssl port program was
+ changed to 128 (from 5).</p>
+ <p>Own Id: OTP-5755 Aux Id: seq10068 </p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Ssl 3.0.7</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>The R/W buffer length i esock.c was too small. It has
+ been increased from 4k to 32k.</p>
+ <p>Own Id: OTP-5620</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Ssl 3.0.6</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>A configuration option for choosing protocol versions has
+ been added (<c>sslv2</c>, <c>sslv3</c>, and
+ <c>tlsv1</c>).</p>
+ <p>Own Id: OTP-5429 Aux Id: seq9755 </p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Ssl 3.0.5</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Linked in drivers in the crypto, and asn1 applications
+ are now compiled with the -D_THREAD_SAFE and -D_REENTRANT
+ switches on unix when the emulator has thread support
+ enabled.</p>
+ <p>Linked in drivers on MacOSX are not compiled with the
+ undocumented -lbundle1.o switch anymore. Thanks to Sean
+ Hinde who sent us a patch.</p>
+ <p>Linked in driver in crypto, and port programs in ssl, now
+ compiles on OSF1.</p>
+ <p>Minor makefile improvements in runtime_tools.</p>
+ <p>Own Id: OTP-5346</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Ssl 3.0.4</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p><c>ssl:recv/3</c> with finite timeout value, closed the
+ connection at timeout.</p>
+ <p>Own Id: OTP-4882</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Ssl 3.0.3</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>When a file descriptor was marked for closing, and 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>
+ </item>
+ <item>
+ <p>When the number of open file descriptors reached
+ FD_SETSIZE, the SSL port program entered a busy loop.</p>
+ <p>Own Id: OTP-5094 Aux Id: seq8806 </p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>The SSL application now supports SSL sessions for
+ servers, which typically speeds up HTTP requests from
+ browsers.</p>
+ <p>Own Id: OTP-5095</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 3.0.2</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>The UTF8String type is now defined in asn1-1.4.4.2 and
+ later. Therefore the definitions of UTF8String has been
+ removed from the ASN.1 modules PKIX1Explicit88.asn1 and
+ PKIXAttributeCertificate.asn1. The SSL application can now
+ only be built using asn-1.4.4.2 or later.</p>
+ <p>OwnId: OTP-4971.</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Known Bugs and Problems</title>
+ <p>See SSL-3.0.
+ </p>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 3.0.1</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>An unexpected object identifier would crash <c>ssl:peercert</c>. </p>
+ <p>OwnId: OTP-4771.</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Known Bugs and Problems</title>
+ <p>See SSL-3.0.
+ </p>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 3.0</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>The <c>cache_timout</c> option was silently ignored. It had
+ to do with SSL sessions, where multiple connections can occur.
+ Since the Erlang SSL application does not support sessions the
+ option is still ignored, and consequently the documentation
+ about it has been removed.</p>
+ <p>OwnId: OTP-3146</p>
+ </item>
+ <item>
+ <p>The Erlang SSL application is now based on OpenSSL version
+ 0.9.7a. OpenSSL 0.9.6 should also work.</p>
+ <p>OwnId: OTP-4002</p>
+ </item>
+ <item>
+ <p>When connecting it is now possible to bind to a local address
+ and local port. </p>
+ <p>OwnId: OTP-4675</p>
+ </item>
+ <item>
+ <p>The <c>ssl_esock</c> port program is now part of the
+ distribution and thus does not have to be created
+ explicitly. It is dynamically linked to OpenSSL
+ libraries in a "standard" location (typically
+ <c>/usr/local/lib</c> on UNIX; in the path on Win32).</p>
+ <p>OwnId:
+ OTP-4676</p>
+ </item>
+ <item>
+ <p>The new functions <c>ssl:peercert/1/2</c> provide information
+ from the certificate of a peer of a connection.</p>
+ <p>OwnId: OTP-4680
+ <br></br>
+Aux Id: seq7688</p>
+ </item>
+ <item>
+ <p>The function <c>ssl:port/1</c> has been removed from the
+ documentation, but not from the <c>ssl</c> interface module.
+ The recommendation is to use <c>ssl:peername/1</c>
+ instead, which provides both address and port of the peer.</p>
+ <p>OwnId: OTP-4681 </p>
+ </item>
+ <item>
+ <p>New User's Guide documentation has been added.</p>
+ <p>OwnId: OTP-4682 </p>
+ </item>
+ <item>
+ <p>The old <c>ssl_socket</c> interface has been removed and also
+ the documentation of it. </p>
+ <p>OwnId: OTP-4683 </p>
+ </item>
+ <item>
+ <p>The use of ephemeral RSA keys is now supported. It is
+ a global configuration option (see the ssl(6) manual page).</p>
+ <p>OwnId: OTP-4691.</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>The option <c>cacertfile</c> is now in effect, and can
+ therefore no longer be set with the OS environment
+ variable SSL_CERT_FILE (which did set the same value for
+ all connections). </p>
+ <p>OwnId: OTP-3146</p>
+ </item>
+ <item>
+ <p>There was a synchronization error at closing of an SSL
+ connection. </p>
+ <p>OwnId: OTP-4435
+ <br></br>
+Aux Id: seq7534</p>
+ </item>
+ <item>
+ <p>C macros in <c>debuglog.c</c> were not ANSI C compliant.</p>
+ <p>OwnId: OTP-4674</p>
+ </item>
+ <item>
+ <p>The <c>binary</c> option was not properly handled.</p>
+ <p>OwnId: OTP-4678</p>
+ </item>
+ <item>
+ <p>The <c>ssl:format_error/1</c> did not consider <c>inet</c>
+ error codes, nor did it have a catch all for unknown error
+ codes.</p>
+ <p>OwnId: OTP-4679</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Known Bugs and Problems</title>
+ <list type="bulleted">
+ <item>
+ <p>Change of controlling process in not OTP compliant. </p>
+ <p>OwnId; OTP-4712</p>
+ </item>
+ <item>
+ <p>There is still no way to restrict the cipher sizes. </p>
+ <p>OwnId: OTP-4712</p>
+ </item>
+ <item>
+ <p>The <c>keep_alive</c> and <c>reuse_addr</c> options will be
+ added in a future release. </p>
+ <p>OwnId: OTP-4677</p>
+ </item>
+ <item>
+ <p>There is currently no way to restrict the SSL/TLS
+ protocol versions to use. In a future release this will be
+ supported as a configuration option, and as an option for
+ each connection as well. </p>
+ <p>OwnId: OTP-4711.</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 2.3.6</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>There was a synchronization error at closing, which could
+ result in that an SSL socket was removed prematurely, resulting
+ in that a user process referring to it received an unexpected
+ exit.</p>
+ <p>OwnId: OTP-4435
+ <br></br>
+Aux Id: seq7600</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Known Bugs and Problems</title>
+ <p>See SSL 2.2 . </p>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 2.3.5</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Setting of the option `nodelay' caused the SSL port program
+ to dump core.</p>
+ <p>OwnId: OTP-4380
+ <br></br>
+Aux Id: -</p>
+ </item>
+ <item>
+ <p>Setting of the option '{active, once}' in <c>setopts</c> was
+ wrong, causing a correct socket message to be regarded as
+ erroneous. </p>
+ <p>OwnId: OTP-4380
+ <br></br>
+Aux Id: -</p>
+ </item>
+ <item>
+ <p>A self-signed peer certificate was always rejected with the
+ error `eselfsignedcert', irrespective of the `depth' value. </p>
+ <p>OwnId: OTP-4374
+ <br></br>
+Aux Id: seq7417</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Known Bugs and Problems</title>
+ <p>See SSL 2.2 . </p>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 2.3.4</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>All TCP options allowed in gen_tcp, are now also allowed in
+ SSL, except the option <c>{reuseaddr, Boolean}</c>. A new
+ function <c>getopts</c> has been added to the SSL interface
+ module <c>ssl</c>. </p>
+ <p>OwnId: OTP-4305, OTP-4159</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 2.3.3</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>The roles of the SSLeay and OpenSSL packages has been
+ clarified in the ssl(6) application manual page. Also
+ the URLs from which to download SSLeay has been updated.</p>
+ <p>OwnId: OTP-4002
+ <br></br>
+Aux Id: seq5269</p>
+ </item>
+ <item>
+ <p>A call to <c>ssl:listen(Port, Options)</c> with
+ <c>Options = []</c> resulted in the cryptic <c>{error, ebadf}</c> return value. The return value has been changed
+ to <c>{error, enooptions}</c>, and the behaviour has been
+ documented in the <c>listen/2</c> function.</p>
+ <p>OwnId: OTP-4016
+ <br></br>
+Aux Id: seq7006</p>
+ </item>
+ <item>
+ <p>Use of the option <c>{nodelay, boolean()}</c> crashed
+ the <c>ssl_server</c>.</p>
+ <p>OwnId: OTP-4070
+ <br></br>
+Aux Id:</p>
+ </item>
+ <item>
+ <p>A bug caused the Erlang distribution over ssl to fail.
+ This bug has now been fixed.</p>
+ <p>OwnId: OTP-4072
+ <br></br>
+Aux Id:</p>
+ </item>
+ <item>
+ <p>On Windows when the SSL port program encountered an
+ error code not anticipated it crashed. </p>
+ <p>OwnId: OTP-4132
+ <br></br>
+Aux Id:</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 2.3.2</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>The <c>ssl:accept/1-2</c> function sometimes returned
+ <c>{error, {What, Where}}</c> instead of <c>{error, What}</c>, where <c>What</c> is an atom. </p>
+ <p>OwnId: OTP-3775
+ <br></br>
+Aux Id: seq4991</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 2.3.1</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Sometimes the SSL portprogram would loop in an accept
+ loop, without terminating even when the SSL application
+ was stopped.. </p>
+ <p>OwnId: OTP-3691</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 2.3</title>
+ <p>Functions have been added to SSL to experimentally support
+ Erlang distribution.
+ </p>
+ </section>
+
+ <section>
+ <title>SSL 2.2.1</title>
+ <p>The 2.2.1 version of SSL provides code replacement in runtime
+ by upgrading from, or downgrading to, versions 2.1 and 2.2.
+ </p>
+ </section>
+
+ <section>
+ <title>SSL 2.2</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>The restriction that only the creator of an SSL socket can
+ read from and write to the socket has been lifted.</p>
+ <p>OwnId: OTP-3301</p>
+ </item>
+ <item>
+ <p>The option <c>{packet, cdr}</c> for SSL sockets has been added,
+ which means that SSL sockets also supports CDR encoded packets.</p>
+ <p>OwnId: OTP-3302</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Known Bugs and Problems</title>
+ <list type="bulleted">
+ <item>
+ <p>Setting of a CA certificate file with the <c>cacertfile</c>
+ option (in calls to <c>ssl:accept/1/2</c> or
+ <c>ssl:connect/3/4</c>) does not work due to weaknesses
+ in the SSLeay package. </p>
+ <p>A work-around is to set the OS environment variable
+ <c>SSL_CERT_FILE</c> before SSL is started. However, then
+ the CA certificate file will be global for all connections.</p>
+ <p>OwnId: OTP-3146</p>
+ </item>
+ <item>
+ <p>When changing controlling process of an SSL socket, a
+ temporary process is started, which is not gen_server
+ compliant.</p>
+ <p>OwnId: OTP-3146</p>
+ </item>
+ <item>
+ <p>Although there is a <c>cache</c> timeout option, it is
+ silently ignored.</p>
+ <p>OwnId: OTP-3146</p>
+ </item>
+ <item>
+ <p>There is currently no way to restrict the cipher sizes.</p>
+ <p>OwnId: OTP-3146</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 2.1</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>The set of possible error reasons has been extended to
+ contain diagnostics on erroneous certificates and failures
+ to verify certificates.</p>
+ <p>OwnId: OTP-3145</p>
+ </item>
+ <item>
+ <p>The maximum number of simultaneous SSL connections on
+ Windows has been increased from 31 to 127.</p>
+ <p>OwnId: OTP-3145</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>A dead-lock occurring when write queues are not empty has
+ been removed. </p>
+ <p>OwnId: OTP-3145</p>
+ </item>
+ <item>
+ <p>Error reasons have been unified and changed.</p>
+ <p>(** POTENTIAL INCOMPATIBILITY **)</p>
+ <p>OwnId: OTP-3145</p>
+ </item>
+ <item>
+ <p>On Windows a check of the existence of the environment
+ variable <c>ERLSRV_SERVICE_NAME</c> has been added. If
+ that variable is defined, the port program of the SSL
+ application will not terminated when a user logs off.</p>
+ <p>OwnId: OTP-3145</p>
+ </item>
+ <item>
+ <p>An error in the setting of the <c>nodelay</c> option
+ has been corrected.</p>
+ <p>OwnId: OTP-3145</p>
+ </item>
+ <item>
+ <p>The confounded notions of verify mode and verify depth has
+ been corrected. The option <c>verifydepth</c> has been
+ removed, and the two separate options <c>verify</c> and
+ <c>depth</c> has been added.</p>
+ <p>(** POTENTIAL INCOMPATIBILITY **)</p>
+ <p>OwnId: OTP-3145</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Known Bugs and Problems</title>
+ <list type="bulleted">
+ <item>
+ <p>Setting of a CA certificate file with the <c>cacertfile</c>
+ option (in calls to <c>ssl:accept/1/2</c> or
+ <c>ssl:connect/3/4</c>) does not work due to weaknesses
+ in the SSLeay package. </p>
+ <p>A work-around is to set the OS environment variable
+ <c>SSL_CERT_FILE</c> before SSL is started. However, then
+ the CA certificate file will be global for all connections.</p>
+ <p>OwnId: OTP-3146</p>
+ </item>
+ <item>
+ <p>When changing controlling process of an SSL socket, a
+ temporary process is started, which is not gen_server
+ compliant.</p>
+ <p>OwnId: OTP-3146</p>
+ </item>
+ <item>
+ <p>Although there is a <c>cache</c> timeout option, it is
+ silently ignored.</p>
+ <p>OwnId: OTP-3146</p>
+ </item>
+ <item>
+ <p>There is currently no way to restrict the cipher sizes.</p>
+ <p>OwnId: OTP-3146</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>SSL 2.0</title>
+ <p>A complete new version of SSL with separate I/O channels
+ for all connections with non-blocking I/O multiplexing.</p>
+ </section>
+</chapter>
+
+
diff --git a/lib/ssl/doc/src/pkix_certs.xml b/lib/ssl/doc/src/pkix_certs.xml
new file mode 100644
index 0000000000..47818c1b7d
--- /dev/null
+++ b/lib/ssl/doc/src/pkix_certs.xml
@@ -0,0 +1,253 @@
+<?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>PKIX Certificates</title>
+ <prepared>UAB/F/P Peter H&ouml;gfeldt</prepared>
+ <docno></docno>
+ <date>2003-06-09</date>
+ <rev>A</rev>
+ <file>pkix_certs.xml</file>
+ </header>
+
+ <section>
+ <title>Introduction to Certificates</title>
+ <p>Certificates were originally defined by ITU (CCITT) and the latest
+ definitions are described in <cite id="X.509"></cite>, but those definitions
+ are (as always) not working.
+ </p>
+ <p>Working certificate definitions for the Internet Community are found
+ in the the PKIX RFCs <cite id="rfc3279"></cite>and <cite id="rfc3280"></cite>.
+ The parsing of certificates in the Erlang/OTP SSL application is
+ based on those RFCS.
+ </p>
+ <p>Certificates are defined in terms of ASN.1 (<cite id="X.680"></cite>).
+ For an introduction to ASN.1 see <url href="http://asn1.elibel.tm.fr/">ASN.1 Information Site</url>.
+ </p>
+ </section>
+
+ <section>
+ <title>PKIX Certificates</title>
+ <p>Here we base the PKIX certificate definitions in RFCs <cite id="rfc3279"></cite>and <cite id="rfc3280"></cite>. We however present the
+ definitions according to <c>SSL-PKIX.asn1</c> module,
+ which is an amelioration of the <c>PKIX1Explicit88.asn1</c>,
+ <c>PKIX1Implicit88.asn1</c>, and <c>PKIX1Algorithms88.asn1</c>
+ modules. You find all these modules in the <c>pkix</c> subdirectory
+ of SSL.
+ </p>
+ <p>The Erlang terms that are returned by the functions
+ <c>ssl:peercert/1/2</c>, <c>ssl_pkix:decode_cert/1/2</c>, and
+ <c>ssl_pkix:decode_cert_file/1/2</c> when the option <c>ssl</c>
+ is used in those functions, correspond the ASN.1 structures
+ described in the sequel.
+ </p>
+
+ <section>
+ <title>Certificate and TBSCertificate</title>
+ <code type="none">
+Certificate ::= SEQUENCE {
+ tbsCertificate TBSCertificate,
+ signatureAlgorithm SignatureAlgorithm,
+ signature BIT STRING }
+
+TBSCertificate ::= 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 -- }
+
+Version ::= INTEGER { v1(0), v2(1), v3(2) }
+
+CertificateSerialNumber ::= INTEGER
+
+Validity ::= SEQUENCE {
+ notBefore Time,
+ notAfter Time }
+
+Time ::= CHOICE {
+ utcTime UTCTime,
+ generalTime GeneralizedTime }
+ </code>
+ <p>The meaning of the fields <c>version</c>, <c>serialNumber</c>,
+ and <c>validity</c> are quite obvious given the type definitions
+ above, so we do not go further into their details.
+ </p>
+ <p>The <c>signatureAlgorithm</c> field of <c>Certificate</c> and
+ the <c>signature</c> field of <c>TBSCertificate</c> contain
+ the name and parameters of the algorithm used for signing the
+ certificate. The values of these two fields must be equal.
+ </p>
+ <p>The <c>signature</c> field of <c>Certificate</c> contains the
+ value of the signature that the issuer computed by using the
+ prescribed algorithm.
+ </p>
+ <p>The <c><![CDATA[issuer<c> and <c>subject]]></c> fields can contain many
+ different types av data, and is therefore considered in a
+ separate section. The same holds for the <c>extensions</c>
+ field.
+ The <c>issuerUniqueID</c> and the <c>subjectUniqueID</c> fields
+ are not considered further.</p>
+ </section>
+
+ <section>
+ <title>TBSCertificate issuer and subject</title>
+ <p></p>
+ <code type="none"><![CDATA[
+Name ::= CHOICE { -- only one possibility for now --
+ rdnSequence RDNSequence }
+
+RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+
+DistinguishedName ::= RDNSequence
+
+RelativeDistinguishedName ::=
+ SET SIZE (1 .. MAX) OF AttributeTypeAndValue
+
+AttributeTypeAndValue ::= SEQUENCE {
+ type ATTRIBUTE-TYPE-AND-VALUE-CLASS.&id
+\011\011({SupportedAttributeTypeAndValues}),
+ value ATTRIBUTE-TYPE-AND-VALUE-CLASS.&Type
+\011\011({SupportedAttributeTypeAndValues}{@type}) }
+
+SupportedAttributeTypeAndValues ATTRIBUTE-TYPE-AND-VALUE-CLASS ::=
+\011{ name | surname | givenName | initials | generationQualifier |
+\011 commonName | localityName | stateOrProvinceName | organizationName |
+\011 organizationalUnitName | title | dnQualifier | countryName |
+\011 serialNumber | pseudonym | domainComponent | emailAddress } ]]></code>
+ </section>
+
+ <section>
+ <title>TBSCertificate extensions</title>
+ <p>The <c>extensions</c> field of a <c>TBScertificate</c> is a
+ sequence of type <c>Extension</c>, defined as follows,</p>
+ <code type="none">
+Extension ::= SEQUENCE {
+ extnID OBJECT IDENTIFIER,
+ critical BOOLEAN DEFAULT FALSE,
+ extnValue ANY } </code>
+ <p>Each extension has a unique object identifier. An extension
+ with a <c>critical</c> value set to <c>TRUE</c><em>must</em>
+ be recognised by the reader of a certificate, or else the
+ certificate must be rejected.
+ </p>
+ <p>Extensions are divided into two groups: standard extensions
+ and internet certificate extensions. All extensions listed in
+ the table that follows are standard extensions, except for
+ <c>authorityInfoAccess</c> and <c>subjectInfoAccess</c>, which
+ are internet extensions.
+ </p>
+ <p>Depending on the object identifier the <c>extnValue</c> is
+ parsed into an appropriate welldefined structure.
+ </p>
+ <p>The following table shows the purpose of each extension, but
+ does not specify the structure. To see the structure consult
+ the <c>PKIX1Implicit88.asn1</c> module.
+ </p>
+ <table>
+ <row>
+ <cell align="left" valign="middle">authorityKeyIdentifier</cell>
+ <cell align="left" valign="middle">Used by to identify a certificate signed that has multiple signing keys. </cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">subjectKeyIdentifier</cell>
+ <cell align="left" valign="middle">Used to identify certificates that contain a public key. Must appear i CA certificates.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">keyUsage </cell>
+ <cell align="left" valign="middle">Defines the purpose of the certificate. Can be one or several of<c>digitalSignature</c>, <c>nonRepudiation</c>,<c>keyEncipherment</c>, <c>dataEncipherment</c>,<c>keyAgreement</c>, <c>keyCertSign</c>, <c>cRLSign</c>,<c>encipherOnly</c>, <c>decipherOnly</c>.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">privateKeyUsagePeriod </cell>
+ <cell align="left" valign="middle">Allows certificate issuer to provide a private key usage period to be short than the certificate usage period.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">certificatePolicies</cell>
+ <cell align="left" valign="middle">Contains one or more policy information terms indicating the policies under which the certificate has been issued.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">policyMappings</cell>
+ <cell align="left" valign="middle">Used i CA certificates. </cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">subjectAltName</cell>
+ <cell align="left" valign="middle">Allows additional identities to be bound the the subject. </cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">issuerAltName</cell>
+ <cell align="left" valign="middle">Allows additional identities to be bound the the issuer.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">subjectDirectoryAttributes</cell>
+ <cell align="left" valign="middle">Conveys identity attributes of the subject.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">basicConstraints</cell>
+ <cell align="left" valign="middle">Tells if the certificate holder is a CA or not.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">nameConstraints</cell>
+ <cell align="left" valign="middle">Used in CA certificates.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">policyConstraints</cell>
+ <cell align="left" valign="middle">Used in CA certificates.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">extKeyUsage</cell>
+ <cell align="left" valign="middle">Indicates for which purposed the public key may be used. </cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">cRLDistributionPoints</cell>
+ <cell align="left" valign="middle">Indicates how CRL (Certificate Revokation List) information is obtained.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">inhibitAnyPolicy</cell>
+ <cell align="left" valign="middle">Used i CA certificates.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">freshestCRL</cell>
+ <cell align="left" valign="middle">For CRLs.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">authorityInfoAccess</cell>
+ <cell align="left" valign="middle">How to access CA information of the issuer of the certificate.</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">subjectInfoAccess</cell>
+ <cell align="left" valign="middle">How to access CA information of the subject of the certificate.</cell>
+ </row>
+ <tcaption>PKIX Extensions</tcaption>
+ </table>
+ </section>
+ </section>
+</chapter>
+
+
diff --git a/lib/ssl/doc/src/refman.xml b/lib/ssl/doc/src/refman.xml
new file mode 100644
index 0000000000..3ad5a01b46
--- /dev/null
+++ b/lib/ssl/doc/src/refman.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE application SYSTEM "application.dtd">
+
+<application xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>1999</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>SSL Reference Manual</title>
+ <prepared>Peter H&ouml;gfeldt</prepared>
+ <docno></docno>
+ <date>2003-03-25</date>
+ <rev>B</rev>
+ <file>refman.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
+ </p>
+ <p>This product includes software written by Tim Hudson
+ </p>
+ <p>For full OpenSSL and SSLeay license texts, see <seealso marker="licenses#licenses">Licenses</seealso>.</p>
+ </description>
+ <xi:include href="ssl_app.xml"/>
+ <xi:include href="ssl.xml"/>
+ <xi:include href="new_ssl.xml"/>
+</application>
+
+
diff --git a/lib/ssl/doc/src/release_notes.xml b/lib/ssl/doc/src/release_notes.xml
new file mode 100644
index 0000000000..e7c766bb91
--- /dev/null
+++ b/lib/ssl/doc/src/release_notes.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>1999</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>SSL Release Notes</title>
+ <prepared>Peter H&ouml;gfeldt</prepared>
+ <docno></docno>
+ <date>2003-05-26</date>
+ <rev>A</rev>
+ <file>release_notes.sgml</file>
+ </header>
+ <description>
+ <p>The SSL 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
+ </p>
+ <p>This product includes software written by Tim Hudson
+ </p>
+ <p>For full OpenSSL and SSLeay license texts, see <seealso marker="licenses#licenses">Licenses</seealso>.
+ </p>
+ </description>
+ <xi:include href="notes.xml"/>
+</part>
+
+
diff --git a/lib/ssl/doc/src/remember.xml b/lib/ssl/doc/src/remember.xml
new file mode 100644
index 0000000000..799627a33c
--- /dev/null
+++ b/lib/ssl/doc/src/remember.xml
@@ -0,0 +1,83 @@
+<?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>PKIX Certificates</title>
+ <prepared>UAB/F/P Peter H&ouml;gfeldt</prepared>
+ <docno></docno>
+ <date>2003-06-09</date>
+ <rev>A</rev>
+ <file>pkix_certs.sgml</file>
+ </header>
+
+ <section>
+ <title>Introduction to Certificates</title>
+ <p><em>Outline:</em></p>
+ <list type="bulleted">
+ <item>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 - main
+ contents of certificate - contents have increased as time went
+ by - common file formats for certificates.
+ </item>
+ <item>private keys - password protection - key generation - file
+ formats.
+ </item>
+ <item>ssl_pkix and alternate decodings.
+ </item>
+ <item>Attribute Certificates (not used by SSL).
+ </item>
+ <item>Certificate requests - certificate authorities - signing of
+ certificates - certificate revocation lists.
+ </item>
+ <item>standards: ASN.1, X.509, X.520, PKIX, PKCS, PEM.
+ </item>
+ <item>incompatibilities between standards (X.509-1997 vs old) - the
+ ASN.1 problem of ANY, BIT STRING and OCTET STRING - the module
+ ssl_pkix.
+ </item>
+ <item>test suites: NIST
+ </item>
+ <item>Warnings: *creation* of trusted certificate (OpenSSL).
+ </item>
+ <item>Erlang SSL and certificates
+ </item>
+ <item>The need for seeding the random generator. See also John
+ S. Denker: High-Entropy Symbol Generator
+ (http://www.monmouth.com/~jsd).
+ </item>
+ <item>links to standards and documents. Books (Rescorla).
+ </item>
+ <item>ASN.1 crash course.
+ </item>
+ <item>Nagel algorithm.
+ </item>
+ </list>
+ <p>For an introduction to ASN.1 see <url href="http://asn1.elibel.tm.fr/">ASN.1 Information Site</url>.
+ </p>
+ </section>
+</chapter>
+
+
diff --git a/lib/ssl/doc/src/ssl.fig b/lib/ssl/doc/src/ssl.fig
new file mode 100644
index 0000000000..a338720fd1
--- /dev/null
+++ b/lib/ssl/doc/src/ssl.fig
@@ -0,0 +1,10 @@
+#FIG 3.1
+Portrait
+Center
+Inches
+1200 2
+2 2 0 1 6 6 0 0 20 0.000 0 0 -1 0 0 5
+ 1350 525 4425 525 4425 2250 1350 2250 1350 525
+4 0 4 0 0 18 48 0.0000 4 555 495 2025 1575 S\001
+4 0 4 0 0 18 48 0.0000 4 555 495 2475 1725 S\001
+4 0 4 0 0 18 48 0.0000 4 555 435 3000 1620 L\001
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
new file mode 100644
index 0000000000..9b780b14ce
--- /dev/null
+++ b/lib/ssl/doc/src/ssl.xml
@@ -0,0 +1,728 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1999</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>ssl</title>
+ <prepared>Peter H&ouml;gfeldt</prepared>
+ <responsible>Peter H&ouml;gfeldt</responsible>
+ <docno></docno>
+ <approved>Peter H&ouml;gfeldt</approved>
+ <checked></checked>
+ <date>2003-03-25</date>
+ <rev>D</rev>
+ <file>ssl.sgml</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>
+ </description>
+
+ <section>
+ <title>General</title>
+
+ <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>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) -> </name>
+ <name>peercert(Socket, Opts) -> {ok, Cert} | {ok, Subject} | {error, Reason}</name>
+ <fsummary>Return the peer certificate.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Opts = [pkix | ssl | subject]()</v>
+ <v>Cert = term()()</v>
+ <v>Subject = term()()</v>
+ </type>
+ <desc>
+ <p><c>peercert(Cert)</c> is equivalent to <c>peercert(Cert, [])</c>.
+ </p>
+ <p>The form of the returned certificate depends on the
+ options.
+ </p>
+ <p>If the options list is empty the certificate is returned as
+ a DER encoded binary.
+ </p>
+ <p>The options <c>pkix</c> and <c>ssl</c> implies that the
+ certificate is returned as a parsed ASN.1 structure in the
+ form of an Erlang term.
+ </p>
+ <p>The <c>ssl</c> option gives a more elaborate return
+ structure, with more explicit information. In particular
+ object identifiers are replaced by atoms.
+ </p>
+ <p>The options <c>pkix</c>, and <c>ssl</c> are mutually
+ exclusive.
+ </p>
+ <p>The option <c>subject</c> implies that only the subject's
+ distinguished name part of the peer certificate is returned.
+ It can only be used together with the option <c>pkix</c> or
+ the option <c>ssl</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)
+ </p>
+ </section>
+
+</erlref>
+
+
diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml
new file mode 100644
index 0000000000..ae8bd87781
--- /dev/null
+++ b/lib/ssl/doc/src/ssl_app.xml
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE appref SYSTEM "appref.dtd">
+
+<appref>
+ <header>
+ <copyright>
+ <year>1999</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>ssl</title>
+ <prepared>Peter H&ouml;gfeldt</prepared>
+ <responsible>Peter H&ouml;gfeldt</responsible>
+ <docno></docno>
+ <approved>Peter H&ouml;gfeldt</approved>
+ <checked>Peter H&ouml;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>
+
+ <section>
+ <title>Environment</title>
+ <p>The following application environment configuration parameters
+ are defined for the SSL application. Refer to application(3) for
+ more information about configuration parameters.
+ </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>
+ <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>
+ <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>
+ </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>
+ <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>
+ </item>
+ <tag><c><![CDATA[proxylsport = integer() | false <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>
+ </item>
+ <tag><c><![CDATA[proxylsbacklog = integer() | false <optional>]]></c></tag>
+ <item>
+ <p>Set the listen queue size of the listen port of the
+ SSL port program. The default is 128.
+ </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>
+ </section>
+
+ <section>
+ <title>SEE ALSO</title>
+ <p>application(3)</p>
+ </section>
+
+</appref>
+
+
diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml
new file mode 100644
index 0000000000..c743cd67a3
--- /dev/null
+++ b/lib/ssl/doc/src/ssl_distribution.xml
@@ -0,0 +1,235 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2000</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>Using SSL for Erlang Distribution</title>
+ <prepared>P Nyblom</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2003-04-01</date>
+ <rev>B</rev>
+ <file>ssl_distribution.xml</file>
+ </header>
+ <p>This chapter describes how the Erlang distribution can use
+ SSL to get additional verification and security.</p>
+
+ <section>
+ <title>Introduction</title>
+ <p>The Erlang distribution can in theory use almost any connection
+ based protocol as bearer. A module that implements the protocol
+ specific parts of connection setup is however needed. The
+ default distribution module is <c>inet_tcp_dist</c> which is
+ included in the Kernel application. When starting an
+ Erlang node distributed, <c>net_kernel</c> uses this module to
+ setup listen ports and connections. </p>
+ <p>In the SSL application there is an additional distribution
+ module, <c>inet_ssl_dist</c> which can be used as an
+ alternative. All distribution connections will be using SSL and
+ 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
+ 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
+ differentiate between two different Erlang networks.</p>
+ <p>Setting up Erlang distribution over SSL involves some simple but
+ necessary steps:</p>
+ <list type="bulleted">
+ <item>Building boot scripts including the SSL application</item>
+ <item>Specifying the distribution module for net_kernel</item>
+ <item>Specifying security options and other SSL options</item>
+ </list>
+ <p>The rest of this chapter describes the above mentioned steps in
+ more detail.</p>
+ </section>
+
+ <section>
+ <title>Building boot scripts including the SSL application</title>
+ <p>Boot scripts are built using the <c>systools</c> utility in the
+ SASL application. Refer to the SASL documentations
+ for more information on systools. This is only an example of
+ what can be done.</p>
+ <p>The simplest boot script possible includes only the Kernel
+ and STDLIB applications. Such a script is located in the
+ Erlang distributions bin directory. The source for the script
+ can be found under the Erlang installation top directory under
+ <c><![CDATA[releases/<OTP version>start_clean.rel]]></c>. Copy that
+ script to another location (and preferably another name)
+ and add the SSL application with its current version number
+ after the STDLIB application.</p>
+ <p>An example .rel file with SSL added may look like this:</p>
+ <code type="none">
+{release, {"OTP APN 181 01","P7A"}, {erts, "5.0"},
+ [{kernel,"2.5"},
+ {stdlib,"1.8.1"},
+ {ssl,"2.2.1"}]}. </code>
+ <p>Note that the version numbers surely will differ in your system.
+ Whenever one of the applications included in the script is
+ upgraded, the script has to be changed.</p>
+ <p>Assuming the above .rel file is stored in a file
+ <c>start_ssl.rel</c> in the current directory, a boot script
+ can be built like this:</p>
+ <code type="none">
+1> systools:make_script("start_ssl",[]). </code>
+ <p>There will now be a file <c>start_ssl.boot</c> in the current
+ directory. To test the boot script, start Erlang with the
+ <c>-boot</c> command line parameter specifying this boot script
+ (with its full path but without the <c>.boot</c> suffix), in
+ Unix it could look like this:</p>
+ <p></p>
+ <code type="none"><![CDATA[
+$ erl -boot /home/me/ssl/start_ssl
+Erlang (BEAM) emulator version 5.0
+
+Eshell V5.0 (abort with ^G)
+1> whereis(ssl_server).
+<0.32.0> ]]></code>
+ <p>The <c>whereis</c> function call verifies that the SSL
+ application is really started.</p>
+ <p>As an alternative to building a bootscript, one can explicitly
+ add the path to the ssl <c>ebin</c> directory on the command
+ line. This is done with the command line option <c>-pa</c>. This
+ works as the ssl application really need not be started for the
+ distribution to come up, a primitive version of the ssl server
+ is started by the distribution module itself, so as long as the
+ primitive code server can reach the code, the distribution will
+ start. The <c>-pa</c> method is only recommended for testing
+ purposes.</p>
+ </section>
+
+ <section>
+ <title>Specifying distribution module for net_kernel</title>
+ <p>The distribution module for SSL is named <c>inet_ssl_dist</c>
+ and is specified on the command line whit the <c>-proto_dist</c>
+ option. The argument to <c>-proto_dist</c> should be the module
+ name without the <c>_dist</c> suffix, so this distribution
+ module is specified with <c>-proto_dist inet_ssl</c> on the
+ command line.</p>
+ <p></p>
+ <p>Extending the command line from above gives us the following:</p>
+ <code type="none">
+$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_ssl </code>
+ <p>For the distribution to actually be started, we need to give
+ the emulator a name as well:</p>
+ <code type="none">
+$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_ssl -sname ssl_test
+Erlang (BEAM) emulator version 5.0 [source]
+
+Eshell V5.0 (abort with ^G)
+(ssl_test@myhost)1> </code>
+ <p>Note however that a node started in this way will refuse to talk
+ to other nodes, as no certificates or key files are supplied
+ (see below).</p>
+ <p>When the SSL distribution starts, the OTP system is in its
+ early boot stage, why neither <c>application</c> nor <c>code</c>
+ are usable. As SSL needs to start a port program in this early
+ stage, it tries to determine the path to that program from the
+ primitive code loaders code path. If this fails, one need to
+ specify the directory where the port program resides. This can
+ be done either with an environment variable
+ <c>ERL_SSL_PORTPROGRAM_DIR</c> or with the command line option
+ <c>-ssl_portprogram_dir</c>. The value should be the directory
+ where the <c>ssl_esock</c> port program is located. Note that
+ this option is never needed in a normal Erlang installation.</p>
+ </section>
+
+ <section>
+ <title>Specifying security options and other SSL options</title>
+ <p>For SSL to work, you either need certificate files or a
+ key file. Certificate files can be specified both when working as
+ client and as server (connecting or accepting). </p>
+ <p></p>
+ <p>On the <c>erl</c> command line one can specify options that the
+ ssl distribution will add when creation a socket. It is
+ mandatory to specify at least a key file or client and server
+ certificates. One can specify any <em>SSL option</em> on the
+ command line, but must not specify any socket options (like
+ packet size and such). The SSL options are listed in the
+ Reference Manual. The only difference between the
+ options in the reference manual and the ones that can be
+ specified to the distribution on the command line is that
+ <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
+ 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
+ SSL options/option values. The <c>-ssl_dist_opt</c> argument can
+ be repeated any number of times.</p>
+ <p>An example command line would now look something like this
+ (line breaks in the command are for readability,
+ they should not be there when typed):</p>
+ <code type="none">
+$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_ssl
+ -ssl_dist_opt client_certfile "/home/me/ssl/erlclient.pem"
+ -ssl_dist_opt server_certfile "/home/me/ssl/erlserver.pem"
+ -ssl_dist_opt verify 1 depth 1
+ -sname ssl_test
+Erlang (BEAM) emulator version 5.0 [source]
+
+Eshell V5.0 (abort with ^G)
+(ssl_test@myhost)1> </code>
+ <p>A node started in this way will be fully functional, using SSL
+ as the distribution protocol.</p>
+ </section>
+
+ <section>
+ <title>Setting up environment to always use SSL</title>
+ <p>A convenient way to specify arguments to Erlang is to use the
+ <c>ERL_FLAGS</c> environment variable. All the flags needed to
+ use SSL distribution can be specified in that variable and will
+ then be interpreted as command line arguments for all
+ subsequent invocations of Erlang.</p>
+ <p></p>
+ <p>In a Unix (Bourne) shell it could look like this (line breaks for
+ readability):</p>
+ <code type="none">
+$ ERL_FLAGS="-boot \\"/home/me/ssl/start_ssl\\" -proto_dist inet_ssl
+ -ssl_dist_opt client_certfile \\"/home/me/ssl/erlclient.pem\\"
+ -ssl_dist_opt server_certfile \\"/home/me/ssl/erlserver.pem\\"
+ -ssl_dist_opt verify 1 -ssl_dist_opt depth 1"
+$ export ERL_FLAGS
+$ erl -sname ssl_test
+Erlang (BEAM) emulator version 5.0 [source]
+
+Eshell V5.0 (abort with ^G)
+(ssl_test@myhost)1> init:get_arguments().
+[{root,["/usr/local/erlang"]},
+ {progname,["erl "]},
+ {sname,["ssl_test"]},
+ {boot,["/home/me/ssl/start_ssl"]},
+ {proto_dist,["inet_ssl"]},
+ {ssl_dist_opt,["client_certfile","/home/me/ssl/erlclient.pem"]},
+ {ssl_dist_opt,["server_certfile","/home/me/ssl/erlserver.pem"]},
+ {ssl_dist_opt,["verify","1"]},
+ {ssl_dist_opt,["depth","1"]},
+ {home,["/home/me"]}] </code>
+ <p>The <c>init:get_arguments()</c> call verifies that the correct
+ arguments are supplied to the emulator. </p>
+ </section>
+</chapter>
+
+
diff --git a/lib/ssl/doc/src/ssl_protocol.xml b/lib/ssl/doc/src/ssl_protocol.xml
new file mode 100644
index 0000000000..3dc2332795
--- /dev/null
+++ b/lib/ssl/doc/src/ssl_protocol.xml
@@ -0,0 +1,349 @@
+<?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>The SSL Protocol</title>
+ <prepared>Peter H&ouml;gfeldt</prepared>
+ <docno></docno>
+ <date>2003-04-28</date>
+ <rev>PA2</rev>
+ <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>
+
+ <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>
+
+ <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>
+ </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>
+ </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>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>
+
+ <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>
+
+
diff --git a/lib/ssl/doc/src/usersguide.xml b/lib/ssl/doc/src/usersguide.xml
new file mode 100644
index 0000000000..98071f5742
--- /dev/null
+++ b/lib/ssl/doc/src/usersguide.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2000</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>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
+ </p>
+ <p>This product includes software written by Tim Hudson
+ </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
new file mode 100644
index 0000000000..ba74dcfef4
--- /dev/null
+++ b/lib/ssl/doc/src/using_ssl.xml
@@ -0,0 +1,113 @@
+<?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>Using the SSL application</title>
+ <prepared>Peter H&ouml;gfeldt</prepared>
+ <docno></docno>
+ <date>2003-04-23</date>
+ <rev>PA2</rev>
+ <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>
+
+ <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>
+
+
+
diff --git a/lib/ssl/doc/src/warning.gif b/lib/ssl/doc/src/warning.gif
new file mode 100644
index 0000000000..96af52360e
--- /dev/null
+++ b/lib/ssl/doc/src/warning.gif
Binary files differ
diff --git a/lib/ssl/ebin/.gitignore b/lib/ssl/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ssl/ebin/.gitignore
diff --git a/lib/ssl/examples/certs/Makefile b/lib/ssl/examples/certs/Makefile
new file mode 100644
index 0000000000..121fcc6950
--- /dev/null
+++ b/lib/ssl/examples/certs/Makefile
@@ -0,0 +1,24 @@
+#
+# %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%
+#
+
+#
+# Invoke with GNU make or clearmake -C gnu.
+#
+
+include $(ERL_TOP)/make/run_make.mk
diff --git a/lib/ssl/examples/certs/Makefile.in b/lib/ssl/examples/certs/Makefile.in
new file mode 100644
index 0000000000..4ea7aaf6dc
--- /dev/null
+++ b/lib/ssl/examples/certs/Makefile.in
@@ -0,0 +1,80 @@
+#
+# %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
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ssl/examples/certs/ebin/.gitignore
diff --git a/lib/ssl/examples/certs/rnd/RAND b/lib/ssl/examples/certs/rnd/RAND
new file mode 100644
index 0000000000..70997bd01f
--- /dev/null
+++ b/lib/ssl/examples/certs/rnd/RAND
Binary files differ
diff --git a/lib/ssl/examples/certs/src/make_certs.erl b/lib/ssl/examples/certs/src/make_certs.erl
new file mode 100644
index 0000000000..c374836568
--- /dev/null
+++ b/lib/ssl/examples/certs/src/make_certs.erl
@@ -0,0 +1,261 @@
+%% 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
+%%
+
+-module(make_certs).
+-export([all/0, all/1]).
+
+-record(dn, {commonName,
+ organizationalUnitName = "Erlang OTP",
+ organizationName = "Ericsson AB",
+ localityName = "Stockholm",
+ countryName = "SE",
+ emailAddress = "[email protected]"}).
+
+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).
+
+%%
+%% 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"].
+
+
diff --git a/lib/ssl/examples/ebin/.gitignore b/lib/ssl/examples/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ssl/examples/ebin/.gitignore
diff --git a/lib/ssl/examples/src/Makefile b/lib/ssl/examples/src/Makefile
new file mode 100644
index 0000000000..46c0507b3a
--- /dev/null
+++ b/lib/ssl/examples/src/Makefile
@@ -0,0 +1,78 @@
+#
+# %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
+# ----------------------------------------------------
+EXTRA_ERLC_FLAGS = +warn_unused_vars
+ERL_COMPILE_FLAGS += $(EXTRA_ERLC_FLAGS)
+
+
+MODULES = client_server
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+# ----------------------------------------------------
+# 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/src
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/ebin
+ (cd ..; tar cf - src ebin | (cd $(RELSYSDIR)/examples; tar xf -))
+ chmod -f -R ug+w $(RELSYSDIR)/examples
+
+release_docs_spec:
+
+
+
+
+
+
+
diff --git a/lib/ssl/examples/src/client_server.erl b/lib/ssl/examples/src/client_server.erl
new file mode 100644
index 0000000000..baf5a9185e
--- /dev/null
+++ b/lib/ssl/examples/src/client_server.erl
@@ -0,0 +1,85 @@
+%%
+%% %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: Example of SSL client and server using example certificates.
+
+-module(client_server).
+
+-export([start/0, start/1, init_connect/1]).
+
+start() ->
+ start([ssl, subject]).
+
+start(CertOpts) ->
+ %% Start ssl application
+ application:start(ssl),
+
+ %% Always seed
+ ssl:seed("ellynatefttidppohjeh"),
+
+ %% Let the current process be the server that listens and accepts
+ %% Listen
+ {ok, LSock} = ssl:listen(0, mk_opts(listen)),
+ {ok, {_, LPort}} = ssl:sockname(LSock),
+ io:fwrite("Listen: port = ~w.~n", [LPort]),
+
+ %% Spawn the client process that connects to the server
+ spawn(?MODULE, init_connect, [{LPort, CertOpts}]),
+
+ %% Accept
+ {ok, ASock} = ssl:transport_accept(LSock),
+ ok = ssl:ssl_accept(ASock),
+ io:fwrite("Accept: accepted.~n"),
+ {ok, Cert} = ssl:peercert(ASock, CertOpts),
+ io:fwrite("Accept: peer cert:~n~p~n", [Cert]),
+ io:fwrite("Accept: sending \"hello\".~n"),
+ ssl:send(ASock, "hello"),
+ {error, closed} = ssl:recv(ASock, 0),
+ io:fwrite("Accept: detected closed.~n"),
+ ssl:close(ASock),
+ io:fwrite("Listen: closing and terminating.~n"),
+ ssl:close(LSock),
+ application:stop(ssl).
+
+
+%% Client connect
+init_connect({LPort, CertOpts}) ->
+ {ok, Host} = inet:gethostname(),
+ {ok, CSock} = ssl:connect(Host, LPort, mk_opts(connect)),
+ io:fwrite("Connect: connected.~n"),
+ {ok, Cert} = ssl:peercert(CSock, CertOpts),
+ io:fwrite("Connect: peer cert:~n~p~n", [Cert]),
+ {ok, Data} = ssl:recv(CSock, 0),
+ io:fwrite("Connect: got data: ~p~n", [Data]),
+ io:fwrite("Connect: closing and terminating.~n"),
+ ssl:close(CSock).
+
+mk_opts(listen) ->
+ mk_opts("server");
+mk_opts(connect) ->
+ mk_opts("client");
+mk_opts(Role) ->
+ Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]),
+ [{active, false},
+ {verify, 2},
+ {depth, 2},
+ {cacertfile, filename:join([Dir, Role, "cacerts.pem"])},
+ {certfile, filename:join([Dir, Role, "cert.pem"])},
+ {keyfile, filename:join([Dir, Role, "key.pem"])}].
+
diff --git a/lib/ssl/include/.gitignore b/lib/ssl/include/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ssl/include/.gitignore
diff --git a/lib/ssl/info b/lib/ssl/info
new file mode 100644
index 0000000000..8f610b891b
--- /dev/null
+++ b/lib/ssl/info
@@ -0,0 +1,2 @@
+group: comm
+short: ssl- Secure Socket Layer.
diff --git a/lib/ssl/internal_doc/ssl-implementation.txt b/lib/ssl/internal_doc/ssl-implementation.txt
new file mode 100644
index 0000000000..e5d6ac8cd0
--- /dev/null
+++ b/lib/ssl/internal_doc/ssl-implementation.txt
@@ -0,0 +1,52 @@
+
+Important modules:
+
+ module behaviour children
+ ------ ---------
+ ssl_app application ssl_sup
+ ssl_sup supervisor ssl_server, ssl_broker_sup
+ ssl_server gen_server -
+ ssl_broker_sup supervisor ssl_broker
+ ssl_broker gen_server -
+
+The ssl_server controls a port program that implements the SSL functionality.
+That port program uses the OpenSSL package.
+
+Each socket has a corresponding broker (listen, accept or connect). A broker
+is created and supervised by the ssl_broker_sup.
+
+All communication is between a user and a broker. The broker communicates
+with the ssl_server, that sends its commands to the port program and handles
+the port program responses, that are distributed to users through the
+brokers.
+
+There is a distinction between commands and data flow between the ssl_server
+and the port program. Each established connection between the user and the
+outside world consists of a local erlang socket (owned by the broker) that
+is read from and written to by the broker. At the other end of the local
+connection is a local socket in the port program.
+
+The "real" socket that connects to the outside world is in the port program
+(including listen sockets). The main purpose of the port program is to
+shuffle data between local sockets and outside world sockets, and detect and
+propagate read and write errors (including detection of closed sockets) to
+the ssl_server.
+
+There is documentation in the ssl_broker.erl module.
+
+There is also documentation in the esock.c and esock_openssl.c files.
+
+The ssl_pem.erl, ssl_pkix.erl and ssl_base64.erl modules are support
+modules for reading SSL certificates. Modules for parsing certificates
+are generated from ASN.1 modules in the `pkix' directory.
+
+The `examples' directory contains functions for generating certificates.
+Those certificates are used in the test suites.
+
+
+
+
+
+
+
+
diff --git a/lib/ssl/pkix/Makefile b/lib/ssl/pkix/Makefile
new file mode 100644
index 0000000000..260361c025
--- /dev/null
+++ b/lib/ssl/pkix/Makefile
@@ -0,0 +1,121 @@
+#
+# %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
new file mode 100644
index 0000000000..0caa158f52
--- /dev/null
+++ b/lib/ssl/pkix/OTP-PKIX.asn1config
@@ -0,0 +1,2 @@
+{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
new file mode 100644
index 0000000000..1c3483d519
--- /dev/null
+++ b/lib/ssl/pkix/OTP-PKIX.set.asn
@@ -0,0 +1,6 @@
+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
new file mode 100755
index 0000000000..547cc2e072
--- /dev/null
+++ b/lib/ssl/pkix/PKCS-1.asn1
@@ -0,0 +1,54 @@
+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
new file mode 100644
index 0000000000..e78de69b0e
--- /dev/null
+++ b/lib/ssl/pkix/PKIX1Algorithms88.asn1
@@ -0,0 +1,274 @@
+ 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
new file mode 100644
index 0000000000..a11793618d
--- /dev/null
+++ b/lib/ssl/pkix/PKIX1Algorithms88.hrl
@@ -0,0 +1,94 @@
+%% 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
new file mode 100644
index 0000000000..9b8068fed0
--- /dev/null
+++ b/lib/ssl/pkix/PKIX1Explicit88.asn1
@@ -0,0 +1,619 @@
+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
new file mode 100644
index 0000000000..5940c1e245
--- /dev/null
+++ b/lib/ssl/pkix/PKIX1Explicit88.hrl
@@ -0,0 +1,163 @@
+%% 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
new file mode 100644
index 0000000000..ced270baf6
--- /dev/null
+++ b/lib/ssl/pkix/PKIX1Implicit88.asn1
@@ -0,0 +1,349 @@
+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
new file mode 100644
index 0000000000..8fa1836284
--- /dev/null
+++ b/lib/ssl/pkix/PKIX1Implicit88.hrl
@@ -0,0 +1,93 @@
+%% 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
new file mode 100644
index 0000000000..7d93e6b37e
--- /dev/null
+++ b/lib/ssl/pkix/PKIXAttributeCertificate.asn1
@@ -0,0 +1,189 @@
+ 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
new file mode 100644
index 0000000000..99389c4852
--- /dev/null
+++ b/lib/ssl/pkix/PKIXAttributeCertificate.hrl
@@ -0,0 +1,64 @@
+%% 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
new file mode 100644
index 0000000000..8be2c15de5
--- /dev/null
+++ b/lib/ssl/pkix/README
@@ -0,0 +1,49 @@
+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
new file mode 100644
index 0000000000..ea6333f953
--- /dev/null
+++ b/lib/ssl/pkix/SSL-PKIX.asn1
@@ -0,0 +1,704 @@
+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
new file mode 100644
index 0000000000..06edc5113a
--- /dev/null
+++ b/lib/ssl/pkix/mk_ssl_pkix_oid.erl
@@ -0,0 +1,94 @@
+-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
new file mode 100644
index 0000000000..ffe82be68b
--- /dev/null
+++ b/lib/ssl/pkix/prebuild.skip
@@ -0,0 +1,5 @@
+PKIX1Algorithms88.asn1db
+PKIXAttributeCertificate.asn1db
+PKIX1Explicit88.asn1db
+SSL-PKIX.asn1db
+PKIX1Implicit88.asn1db
diff --git a/lib/ssl/priv/bin/.gitignore b/lib/ssl/priv/bin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ssl/priv/bin/.gitignore
diff --git a/lib/ssl/priv/obj/.gitignore b/lib/ssl/priv/obj/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ssl/priv/obj/.gitignore
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
new file mode 100644
index 0000000000..fabf8a4e0d
--- /dev/null
+++ b/lib/ssl/src/Makefile
@@ -0,0 +1,142 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-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
+# ----------------------------------------------------
+
+MODULES= \
+ ssl \
+ ssl_alert \
+ ssl_app \
+ ssl_broker \
+ ssl_broker_sup \
+ ssl_server \
+ ssl_sup \
+ ssl_prim \
+ ssl_pkix \
+ ssl_pem \
+ ssl_base64 \
+ inet_ssl_dist \
+ ssl_certificate\
+ ssl_certificate_db\
+ ssl_cipher \
+ ssl_connection \
+ ssl_connection_sup \
+ ssl_debug \
+ ssl_handshake \
+ ssl_manager \
+ ssl_session \
+ ssl_session_cache_api \
+ ssl_session_cache \
+ ssl_record \
+ ssl_ssl2 \
+ ssl_ssl3 \
+ ssl_tls1 \
+
+INTERNAL_HRL_FILES = \
+ ssl_int.hrl ssl_broker_int.hrl ssl_debug.hrl \
+ 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))
+
+APP_FILE= ssl.app
+APPUP_FILE= ssl.appup
+
+APP_SRC= $(APP_FILE).src
+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)\"
+
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(PUBLIC_HRL_FILES)
+
+clean:
+ rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f errs core *~
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(PUBLIC_HRL_FILES):
+ cp -f $(PUBLIC_HRL_FILES) $(INCLUDE)
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+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
+
+release_docs_spec:
+
+
+
+
+
+
+
diff --git a/lib/ssl/src/inet_ssl_dist.erl b/lib/ssl/src/inet_ssl_dist.erl
new file mode 100644
index 0000000000..f62aefd35a
--- /dev/null
+++ b/lib/ssl/src/inet_ssl_dist.erl
@@ -0,0 +1,449 @@
+%%<copyright>
+%% <year>2000-2008</year>
+%% <holder>Ericsson AB, All Rights Reserved</holder>
+%%</copyright>
+%%<legalnotice>
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson AB.
+%%</legalnotice>
+%%
+-module(inet_ssl_dist).
+
+%% Handles the connection setup phase with other Erlang nodes.
+
+-export([childspecs/0, listen/1, accept/1, accept_connection/5,
+ setup/5, close/1, select/1, is_node_name/1]).
+
+%% internal exports
+
+-export([accept_loop/2,do_accept/6,do_setup/6, getstat/1,tick/1]).
+
+-import(error_logger,[error_msg/2]).
+
+-include("net_address.hrl").
+
+
+
+-define(to_port(Socket, Data, Opts),
+ case ssl_prim:send(Socket, Data, Opts) of
+ {error, closed} ->
+ self() ! {ssl_closed, Socket},
+ {error, closed};
+ R ->
+ R
+ end).
+
+
+-include("dist.hrl").
+-include("dist_util.hrl").
+
+%% -------------------------------------------------------------
+%% This function should return a valid childspec, so that
+%% the primitive ssl_server gets supervised
+%% -------------------------------------------------------------
+childspecs() ->
+ {ok, [{ssl_server_prim,{ssl_server, start_link_prim, []},
+ permanent, 2000, worker, [ssl_server]}]}.
+
+
+%% ------------------------------------------------------------
+%% Select this protocol based on node name
+%% select(Node) => Bool
+%% ------------------------------------------------------------
+
+select(Node) ->
+ case split_node(atom_to_list(Node), $@, []) of
+ [_,_Host] -> true;
+ _ -> false
+ end.
+
+%% ------------------------------------------------------------
+%% Create the listen socket, i.e. the port that this erlang
+%% node is accessible through.
+%% ------------------------------------------------------------
+
+listen(Name) ->
+ case ssl_prim:listen(0, [{active, false}, {packet,4}] ++
+ get_ssl_options(server)) of
+ {ok, Socket} ->
+ TcpAddress = get_tcp_address(Socket),
+ {_,Port} = TcpAddress#net_address.address,
+ {ok, Creation} = erl_epmd:register_node(Name, Port),
+ {ok, {Socket, TcpAddress, Creation}};
+ Error ->
+ Error
+ end.
+
+%% ------------------------------------------------------------
+%% Accepts new connection attempts from other Erlang nodes.
+%% ------------------------------------------------------------
+
+accept(Listen) ->
+ spawn_link(?MODULE, accept_loop, [self(), Listen]).
+
+accept_loop(Kernel, Listen) ->
+ process_flag(priority, max),
+ case ssl_prim:accept(Listen) of
+ {ok, Socket} ->
+ Kernel ! {accept,self(),Socket,inet,ssl},
+ controller(Kernel, Socket),
+ accept_loop(Kernel, Listen);
+ Error ->
+ exit(Error)
+ end.
+
+controller(Kernel, Socket) ->
+ receive
+ {Kernel, controller, Pid} ->
+ flush_controller(Pid, Socket),
+ ssl_prim:controlling_process(Socket, Pid),
+ flush_controller(Pid, Socket),
+ Pid ! {self(), controller};
+ {Kernel, unsupported_protocol} ->
+ exit(unsupported_protocol)
+ end.
+
+flush_controller(Pid, Socket) ->
+ receive
+ {ssl, Socket, Data} ->
+ Pid ! {ssl, Socket, Data},
+ flush_controller(Pid, Socket);
+ {ssl_closed, Socket} ->
+ Pid ! {ssl_closed, Socket},
+ flush_controller(Pid, Socket)
+ after 0 ->
+ ok
+ end.
+
+%% ------------------------------------------------------------
+%% Accepts a new connection attempt from another Erlang node.
+%% Performs the handshake with the other side.
+%% ------------------------------------------------------------
+
+accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
+ spawn_link(?MODULE, do_accept,
+ [self(), AcceptPid, Socket, MyNode,
+ Allowed, SetupTime]).
+
+do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
+ process_flag(priority, max),
+ receive
+ {AcceptPid, controller} ->
+ Timer = dist_util:start_timer(SetupTime),
+ case check_ip(Socket) of
+ true ->
+ HSData = #hs_data{
+ kernel_pid = Kernel,
+ this_node = MyNode,
+ socket = Socket,
+ timer = Timer,
+ this_flags = 0,
+ allowed = Allowed,
+ f_send = fun(S,D) -> ssl_prim:send(S,D) end,
+ f_recv = fun(S,N,T) -> ssl_prim:recv(S,N,T)
+ end,
+ f_setopts_pre_nodeup =
+ fun(S) ->
+ ssl_prim:setopts(S,
+ [{active, false}])
+ end,
+ f_setopts_post_nodeup =
+ fun(S) ->
+ ssl_prim:setopts(S,
+ [{deliver, port},
+ {active, true}])
+ end,
+ f_getll = fun(S) ->
+ ssl_prim:getll(S)
+ end,
+ f_address = fun get_remote_id/2,
+ mf_tick = {?MODULE, tick},
+ mf_getstat = {?MODULE,getstat}
+ },
+ dist_util:handshake_other_started(HSData);
+ {false,IP} ->
+ error_msg("** Connection attempt from "
+ "disallowed IP ~w ** ~n", [IP]),
+ ?shutdown(no_node)
+ end
+ end.
+
+%% ------------------------------------------------------------
+%% Get remote information about a Socket.
+%% ------------------------------------------------------------
+
+get_remote_id(Socket, Node) ->
+ {ok, Address} = ssl_prim:peername(Socket),
+ [_, Host] = split_node(atom_to_list(Node), $@, []),
+ #net_address {
+ address = Address,
+ host = Host,
+ protocol = ssl,
+ family = inet }.
+
+%% ------------------------------------------------------------
+%% Setup a new connection to another Erlang node.
+%% Performs the handshake with the other side.
+%% ------------------------------------------------------------
+
+setup(Node, Type, MyNode, LongOrShortNames,SetupTime) ->
+ spawn_link(?MODULE, do_setup, [self(),
+ Node,
+ Type,
+ MyNode,
+ LongOrShortNames,
+ SetupTime]).
+
+do_setup(Kernel, Node, Type, MyNode, LongOrShortNames,SetupTime) ->
+ process_flag(priority, max),
+ ?trace("~p~n",[{inet_ssl_dist,self(),setup,Node}]),
+ [Name, Address] = splitnode(Node, LongOrShortNames),
+ case inet:getaddr(Address, inet) of
+ {ok, Ip} ->
+ Timer = dist_util:start_timer(SetupTime),
+ case erl_epmd:port_please(Name, Ip) of
+ {port, TcpPort, Version} ->
+ ?trace("port_please(~p) -> version ~p~n",
+ [Node,Version]),
+ dist_util:reset_timer(Timer),
+ case ssl_prim:connect(Ip, TcpPort,
+ [{active, false},
+ {packet,4}] ++
+ get_ssl_options(client)) of
+ {ok, Socket} ->
+ HSData = #hs_data{
+ kernel_pid = Kernel,
+ other_node = Node,
+ this_node = MyNode,
+ socket = Socket,
+ timer = Timer,
+ this_flags = 0,
+ other_version = Version,
+ f_send = fun(S,D) ->
+ ssl_prim:send(S,D)
+ end,
+ f_recv = fun(S,N,T) ->
+ ssl_prim:recv(S,N,T)
+ end,
+ f_setopts_pre_nodeup =
+ fun(S) ->
+ ssl_prim:setopts
+ (S,
+ [{active, false}])
+ end,
+ f_setopts_post_nodeup =
+ fun(S) ->
+ ssl_prim:setopts
+ (S,
+ [{deliver, port},{active, true}])
+ end,
+ f_getll = fun(S) ->
+ ssl_prim:getll(S)
+ end,
+ f_address =
+ fun(_,_) ->
+ #net_address {
+ address = {Ip,TcpPort},
+ host = Address,
+ protocol = ssl,
+ family = inet}
+ end,
+ mf_tick = {?MODULE, tick},
+ mf_getstat = {?MODULE,getstat},
+ request_type = Type
+ },
+ dist_util:handshake_we_started(HSData);
+ _ ->
+ %% Other Node may have closed since
+ %% port_please !
+ ?trace("other node (~p) "
+ "closed since port_please.~n",
+ [Node]),
+ ?shutdown(Node)
+ end;
+ _ ->
+ ?trace("port_please (~p) "
+ "failed.~n", [Node]),
+ ?shutdown(Node)
+ end;
+ _Other ->
+ ?trace("inet_getaddr(~p) "
+ "failed (~p).~n", [Node,Other]),
+ ?shutdown(Node)
+ end.
+
+%%
+%% Close a socket.
+%%
+close(Socket) ->
+ ssl_prim:close(Socket).
+
+
+%% If Node is illegal terminate the connection setup!!
+splitnode(Node, LongOrShortNames) ->
+ case split_node(atom_to_list(Node), $@, []) of
+ [Name|Tail] when Tail =/= [] ->
+ Host = lists:append(Tail),
+ case split_node(Host, $., []) of
+ [_] when LongOrShortNames == longnames ->
+ error_msg("** System running to use "
+ "fully qualified "
+ "hostnames **~n"
+ "** Hostname ~s is illegal **~n",
+ [Host]),
+ ?shutdown(Node);
+ [_, _ | _] when LongOrShortNames == shortnames ->
+ error_msg("** System NOT running to use fully qualified "
+ "hostnames **~n"
+ "** Hostname ~s is illegal **~n",
+ [Host]),
+ ?shutdown(Node);
+ _ ->
+ [Name, Host]
+ end;
+ [_] ->
+ error_msg("** Nodename ~p illegal, no '@' character **~n",
+ [Node]),
+ ?shutdown(Node);
+ _ ->
+ error_msg("** Nodename ~p illegal **~n", [Node]),
+ ?shutdown(Node)
+ end.
+
+split_node([Chr|T], Chr, Ack) -> [lists:reverse(Ack)|split_node(T, Chr, [])];
+split_node([H|T], Chr, Ack) -> split_node(T, Chr, [H|Ack]);
+split_node([], _, Ack) -> [lists:reverse(Ack)].
+
+%% ------------------------------------------------------------
+%% Fetch local information about a Socket.
+%% ------------------------------------------------------------
+get_tcp_address(Socket) ->
+ {ok, Address} = ssl_prim:sockname(Socket),
+ {ok, Host} = inet:gethostname(),
+ #net_address {
+ address = Address,
+ host = Host,
+ protocol = ssl,
+ family = inet
+ }.
+
+%% ------------------------------------------------------------
+%% Do only accept new connection attempts from nodes at our
+%% own LAN, if the check_ip environment parameter is true.
+%% ------------------------------------------------------------
+check_ip(Socket) ->
+ case application:get_env(check_ip) of
+ {ok, true} ->
+ case get_ifs(Socket) of
+ {ok, IFs, IP} ->
+ check_ip(IFs, IP);
+ _ ->
+ ?shutdown(no_node)
+ end;
+ _ ->
+ true
+ end.
+
+get_ifs(Socket) ->
+ case ssl_prim:peername(Socket) of
+ {ok, {IP, _}} ->
+ case ssl_prim:getif(Socket) of
+ {ok, IFs} -> {ok, IFs, IP};
+ Error -> Error
+ end;
+ Error ->
+ Error
+ end.
+
+check_ip([{OwnIP, _, Netmask}|IFs], PeerIP) ->
+ case {mask(Netmask, PeerIP), mask(Netmask, OwnIP)} of
+ {M, M} -> true;
+ _ -> check_ip(IFs, PeerIP)
+ end;
+check_ip([], PeerIP) ->
+ {false, PeerIP}.
+
+mask({M1,M2,M3,M4}, {IP1,IP2,IP3,IP4}) ->
+ {M1 band IP1,
+ M2 band IP2,
+ M3 band IP3,
+ M4 band IP4}.
+
+is_node_name(Node) when is_atom(Node) ->
+ case split_node(atom_to_list(Node), $@, []) of
+ [_, _Host] -> true;
+ _ -> false
+ end;
+is_node_name(_Node) ->
+ false.
+tick(Sock) ->
+ ?to_port(Sock,[],[force]).
+getstat(Socket) ->
+ case ssl_prim:getstat(Socket, [recv_cnt, send_cnt, send_pend]) of
+ {ok, Stat} ->
+ split_stat(Stat,0,0,0);
+ Error ->
+ Error
+ end.
+
+split_stat([{recv_cnt, R}|Stat], _, W, P) ->
+ split_stat(Stat, R, W, P);
+split_stat([{send_cnt, W}|Stat], R, _, P) ->
+ split_stat(Stat, R, W, P);
+split_stat([{send_pend, P}|Stat], R, W, _) ->
+ split_stat(Stat, R, W, P);
+split_stat([], R, W, P) ->
+ {ok, R, W, P}.
+
+
+get_ssl_options(Type) ->
+ case init:get_argument(ssl_dist_opt) of
+ {ok, Args} ->
+ ssl_options(Type, Args);
+ _ ->
+ []
+ end.
+
+ssl_options(_,[]) ->
+ [];
+ssl_options(server, [["server_certfile", Value]|T]) ->
+ [{certfile, Value} | ssl_options(server,T)];
+ssl_options(client, [["client_certfile", Value]|T]) ->
+ [{certfile, Value} | ssl_options(client,T)];
+ssl_options(server, [["server_cacertfile", Value]|T]) ->
+ [{cacertfile, Value} | ssl_options(server,T)];
+ssl_options(server, [["server_keyfile", Value]|T]) ->
+ [{keyfile, Value} | ssl_options(server,T)];
+ssl_options(Type, [["client_certfile", _Value]|T]) ->
+ ssl_options(Type,T);
+ssl_options(Type, [["server_certfile", _Value]|T]) ->
+ ssl_options(Type,T);
+ssl_options(Type, [[Item, Value]|T]) ->
+ [{atomize(Item),fixup(Value)} | ssl_options(Type,T)];
+ssl_options(Type, [[Item,Value |T1]|T2]) ->
+ ssl_options(atomize(Type),[[Item,Value],T1|T2]);
+ssl_options(_,_) ->
+ exit(malformed_ssl_dist_opt).
+
+fixup(Value) ->
+ case catch list_to_integer(Value) of
+ {'EXIT',_} ->
+ Value;
+ Int ->
+ Int
+ end.
+
+atomize(List) when is_list(List) ->
+ list_to_atom(List);
+atomize(Atom) when is_atom(Atom) ->
+ Atom.
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
new file mode 100644
index 0000000000..2a7d451341
--- /dev/null
+++ b/lib/ssl/src/ssl.app.src
@@ -0,0 +1,41 @@
+{application, ssl,
+ [{description, "Erlang/OTP SSL application"},
+ {vsn, "%VSN%"},
+ {modules, [ssl,
+ ssl_app,
+ ssl_sup,
+ ssl_server,
+ ssl_broker,
+ ssl_broker_sup,
+ ssl_base64,
+ ssl_pem,
+ ssl_pkix,
+ ssl_pkix_oid,
+ ssl_prim,
+ inet_ssl_dist,
+ ssl_tls1,
+ ssl_ssl3,
+ ssl_ssl2,
+ ssl_session,
+ ssl_session_cache_api,
+ ssl_session_cache,
+ ssl_record,
+ ssl_manager,
+ ssl_handshake,
+ ssl_debug,
+ ssl_connection_sup,
+ ssl_connection,
+ ssl_cipher,
+ ssl_certificate_db,
+ ssl_certificate,
+ ssl_alert,
+ 'OTP-PKIX'
+ ]},
+ {registered, [ssl_sup, ssl_server, ssl_broker_sup]},
+ {applications, [kernel, stdlib]},
+ {env, []},
+ {mod, {ssl_app, []}}]}.
+
+
+
+
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
new file mode 100644
index 0000000000..755c3b51b5
--- /dev/null
+++ b/lib/ssl/src/ssl.appup.src
@@ -0,0 +1,21 @@
+%% -*- 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", [{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}]}
+ ]}.
+
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
new file mode 100644
index 0000000000..1222fe97fd
--- /dev/null
+++ b/lib/ssl/src/ssl.erl
@@ -0,0 +1,841 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-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 : Main API module for SSL.
+
+-module(ssl).
+
+-export([start/0, start/1, stop/0, transport_accept/1,
+ transport_accept/2, ssl_accept/1, ssl_accept/2, ssl_accept/3,
+ ciphers/0, cipher_suites/0, cipher_suites/1, close/1, shutdown/2,
+ connect/3, connect/2, connect/4, connection_info/1,
+ controlling_process/2, listen/2, pid/1, peername/1, recv/2, recv/3,
+ send/2, getopts/2, setopts/2, seed/1, sockname/1, peercert/1,
+ peercert/2, version/0, versions/0, session_info/1, format_error/1]).
+
+%% Should be deprecated as soon as old ssl is removed
+%%-deprecated({pid, 1, next_major_release}).
+
+-include("ssl_int.hrl").
+-include("ssl_internal.hrl").
+
+-record(config, {ssl, %% SSL parameters
+ inet_user, %% User set inet options
+ emulated, %% #socket_option{} emulated
+ inet_ssl, %% inet options for internal ssl socket
+ cb %% Callback info
+ }).
+
+%%--------------------------------------------------------------------
+%% Function: start([, Type]) -> ok
+%%
+%% Type = permanent | transient | temporary
+%% Vsns = [Vsn]
+%% Vsn = ssl3 | tlsv1 | 'tlsv1.1'
+%%
+%% Description: Starts the ssl application. Default type
+%% is temporary. see application(3)
+%%--------------------------------------------------------------------
+start() ->
+ application:start(ssl).
+start(Type) ->
+ application:start(ssl, Type).
+
+%%--------------------------------------------------------------------
+%% Function: stop() -> ok
+%%
+%% Description: Stops the ssl application.
+%%--------------------------------------------------------------------
+stop() ->
+ application:stop(ssl).
+
+%%--------------------------------------------------------------------
+%% Function: connect(Address, Port, Options[, Timeout]) -> {ok, Socket}
+%%
+%% Description: Connect to a ssl server.
+%%--------------------------------------------------------------------
+connect(Socket, SslOptions) when is_port(Socket) ->
+ connect(Socket, SslOptions, infinity).
+
+connect(Socket, SslOptions0, Timeout) when is_port(Socket) ->
+ EmulatedOptions = emulated_options(),
+ {ok, InetValues} = inet:getopts(Socket, EmulatedOptions),
+ inet:setopts(Socket, internal_inet_values()),
+ try handle_options(SslOptions0 ++ InetValues, client) of
+ {ok, #config{cb=CbInfo, ssl=SslOptions, emulated=EmOpts}} ->
+ case inet:peername(Socket) of
+ {ok, {Address, Port}} ->
+ ssl_connection:connect(Address, Port, Socket,
+ {SslOptions, EmOpts},
+ self(), CbInfo, Timeout);
+ {error, Error} ->
+ {error, Error}
+ end
+ catch
+ _:{error, Reason} ->
+ {error, Reason}
+ end;
+
+connect(Address, Port, Options) ->
+ connect(Address, Port, Options, infinity).
+
+connect(Address, Port, Options0, Timeout) ->
+ case proplists:get_value(ssl_imp, Options0, old) of
+ new ->
+ new_connect(Address, Port, Options0, Timeout);
+ old ->
+ %% Allow the option reuseaddr to be present
+ %% so that new and old ssl can be run by the same
+ %% code, however the option will be ignored by old ssl
+ %% 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);
+ Value ->
+ {error, {eoptions, {ssl_imp, Value}}}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: listen(Port, Options) -> {ok, ListenSock} | {error, Reason}
+%%
+%% Description: Creates a ssl listen socket.
+%%--------------------------------------------------------------------
+listen(_Port, []) ->
+ {error, enooptions};
+listen(Port, Options0) ->
+ case proplists:get_value(ssl_imp, Options0, old) of
+ new ->
+ new_listen(Port, Options0);
+ old ->
+ %% Allow the option reuseaddr to be present
+ %% so that new and old ssl can be run by the same
+ %% code, however the option will be ignored by old ssl
+ %% that hardcodes reuseaddr to true in its portprogram.
+ Options = proplists:delete(reuseaddr, Options0),
+ old_listen(Port, Options);
+ Value ->
+ {error, {eoptions, {ssl_imp, Value}}}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: transport_accept(ListenSocket[, Timeout]) -> {ok, Socket}.
+%%
+%% Description: Performs transport accept on a ssl listen socket
+%%--------------------------------------------------------------------
+transport_accept(ListenSocket) ->
+ transport_accept(ListenSocket, infinity).
+
+transport_accept(#sslsocket{pid = {ListenSocket, #config{cb=CbInfo, ssl=SslOpts}},
+ fd = new_ssl} = SslSocket, 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),
+ {CbModule,_,_} = CbInfo,
+ {ok, Socket} = CbModule:accept(ListenSocket, Timeout),
+ inet:setopts(Socket, internal_inet_values()),
+ {ok, Port} = inet:port(Socket),
+ case ssl_connection_sup:start_child([server, "localhost", Port, Socket,
+ {SslOpts, socket_options(InetValues)}, self(),
+ CbInfo]) of
+ {ok, Pid} ->
+ CbModule:controlling_process(Socket, Pid),
+ {ok, SslSocket#sslsocket{pid = Pid}};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+
+transport_accept(#sslsocket{} = ListenSocket, Timeout) ->
+ ensure_old_ssl_started(),
+ {ok, Pid} = ssl_broker:start_broker(acceptor),
+ ssl_broker:transport_accept(Pid, ListenSocket, Timeout).
+
+%%--------------------------------------------------------------------
+%% Function: ssl_accept(ListenSocket[, Timeout]) -> {ok, Socket} |
+%% {error, Reason}
+%%
+%% Description: Performs accept on a ssl listen socket. e.i. performs
+%% ssl handshake.
+%%--------------------------------------------------------------------
+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(ListenSocket, SslOptions) when is_port(ListenSocket) ->
+ ssl_accept(ListenSocket, SslOptions, infinity);
+
+%% Old ssl
+ssl_accept(#sslsocket{} = Socket, Timeout) ->
+ ensure_old_ssl_started(),
+ ssl_broker:ssl_accept(Socket, Timeout).
+
+ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
+ EmulatedOptions = emulated_options(),
+ {ok, InetValues} = inet:getopts(Socket, EmulatedOptions),
+ inet:setopts(Socket, internal_inet_values()),
+ 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)
+ catch
+ Error = {error, _Reason} -> Error
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: close() -> ok
+%%
+%% Description: Close a ssl connection
+%%--------------------------------------------------------------------
+close(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _}}}, fd = new_ssl}) ->
+ CbMod:close(ListenSocket);
+close(#sslsocket{pid = Pid, fd = new_ssl}) ->
+ ssl_connection:close(Pid);
+close(Socket = #sslsocket{}) ->
+ ensure_old_ssl_started(),
+ ssl_broker:close(Socket).
+
+%%--------------------------------------------------------------------
+%% Function: send(Socket, Data) -> ok
+%%
+%% Description: Sends data over the ssl connection
+%%--------------------------------------------------------------------
+send(#sslsocket{pid = Pid, fd = new_ssl}, Data) ->
+ ssl_connection:send(Pid, Data);
+
+send(#sslsocket{} = Socket, Data) ->
+ ensure_old_ssl_started(),
+ ssl_broker:send(Socket, Data).
+
+%%--------------------------------------------------------------------
+%% Function: recv(Socket, Length [,Timeout]) -> {ok, Data} | {error, reason}
+%%
+%% Description: Receives data when active = false
+%%--------------------------------------------------------------------
+recv(Socket, Length) ->
+ recv(Socket, Length, infinity).
+recv(#sslsocket{pid = Pid, fd = new_ssl}, Length, Timeout) ->
+ ssl_connection:recv(Pid, Length, Timeout);
+
+recv(Socket = #sslsocket{}, Length, Timeout) ->
+ ensure_old_ssl_started(),
+ ssl_broker:recv(Socket, Length, Timeout).
+
+%%--------------------------------------------------------------------
+%% Function: controlling_process(Socket, NewOwner) -> ok | {error, Reason}
+%%
+%% Description: Changes process that receives the messages when active = true
+%% or once.
+%%--------------------------------------------------------------------
+controlling_process(#sslsocket{pid = Pid, fd = new_ssl}, NewOwner)
+ when is_pid(Pid) ->
+ ssl_connection:new_user(Pid, NewOwner);
+
+controlling_process(Socket, NewOwner) when is_pid(NewOwner) ->
+ ensure_old_ssl_started(),
+ 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}
+%%
+%%
+%% Description: Returns ssl protocol and cipher used for the connection
+%%--------------------------------------------------------------------
+connection_info(#sslsocket{pid = Pid, fd = new_ssl}) ->
+ ssl_connection:info(Pid);
+
+connection_info(#sslsocket{} = Socket) ->
+ ensure_old_ssl_started(),
+ ssl_broker:connection_info(Socket).
+
+%%--------------------------------------------------------------------
+%% Function: peercert(Socket[, Opts]) -> {ok, Cert} | {error, Reason}
+%%
+%% Description:
+%%--------------------------------------------------------------------
+peercert(Socket) ->
+ peercert(Socket, []).
+
+peercert(#sslsocket{pid = Pid, fd = new_ssl}, Opts) ->
+ case ssl_connection:peer_certificate(Pid) of
+ {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;
+ {error, Reason} ->
+ {error, Reason}
+ end;
+
+peercert(#sslsocket{} = Socket, Opts) ->
+ ensure_old_ssl_started(),
+ case ssl_broker:peercert(Socket) of
+ {ok, Bin} ->
+ ssl_pkix:decode_cert(Bin, Opts);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: peername(Socket) -> {ok, {Address, Port}} | {error, Reason}
+%%
+%% Description:
+%%--------------------------------------------------------------------
+peername(#sslsocket{fd = new_ssl, pid = Pid}) ->
+ ssl_connection:peername(Pid);
+
+peername(#sslsocket{} = Socket) ->
+ ensure_old_ssl_started(),
+ ssl_broker:peername(Socket).
+
+%%--------------------------------------------------------------------
+%% Function: cipher_suites() ->
+%%
+%% Description:
+%%--------------------------------------------------------------------
+cipher_suites() ->
+ cipher_suites(erlang).
+
+cipher_suites(erlang) ->
+ Version = ssl_record:highest_protocol_version([]),
+ [ssl_cipher:suite_definition(S) || S <- ssl_cipher:suites(Version)];
+
+cipher_suites(openssl) ->
+ Version = ssl_record:highest_protocol_version([]),
+ [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)].
+
+%%--------------------------------------------------------------------
+%% Function: getopts(Socket, OptTags) -> {ok, Options} | {error, Reason}
+%%
+%% Description:
+%%--------------------------------------------------------------------
+getopts(#sslsocket{fd = new_ssl, pid = Pid}, OptTags) when is_pid(Pid) ->
+ ssl_connection:get_opts(Pid, OptTags);
+getopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptTags) ->
+ inet:getopts(ListenSocket, OptTags);
+getopts(#sslsocket{} = Socket, Options) ->
+ ensure_old_ssl_started(),
+ ssl_broker:getopts(Socket, Options).
+
+%%--------------------------------------------------------------------
+%% Function: setopts(Socket, Options) -> ok | {error, Reason}
+%%
+%% Description:
+%%--------------------------------------------------------------------
+setopts(#sslsocket{fd = new_ssl, pid = Pid}, Options) when is_pid(Pid) ->
+ ssl_connection:set_opts(Pid, Options);
+setopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptTags) ->
+ inet:setopts(ListenSocket, OptTags);
+setopts(#sslsocket{} = Socket, Options) ->
+ ensure_old_ssl_started(),
+ ssl_broker:setopts(Socket, Options).
+
+%%---------------------------------------------------------------
+%% Function: shutdown(Socket, How) -> ok | {error, Reason}
+%%
+%% Description: Same as gen_tcp:shutdown/2
+%%--------------------------------------------------------------------
+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}
+%%
+%% Description: Same as inet:sockname/1
+%%--------------------------------------------------------------------
+sockname(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}) ->
+ inet:sockname(ListenSocket);
+
+sockname(#sslsocket{fd = new_ssl, pid = Pid}) ->
+ ssl_connection:sockname(Pid);
+
+sockname(#sslsocket{} = Socket) ->
+ ensure_old_ssl_started(),
+ ssl_broker:sockname(Socket).
+
+%%---------------------------------------------------------------
+%% Function: seed(Data) -> ok | {error, edata}
+%%
+%% Description:
+%%--------------------------------------------------------------------
+%% TODO: crypto:seed ?
+seed(Data) ->
+ ensure_old_ssl_started(),
+ ssl_server:seed(Data).
+
+%%---------------------------------------------------------------
+%% Function: session_id(Socket) -> {ok, PropList} | {error, Reason}
+%%
+%% Description:
+%%--------------------------------------------------------------------
+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'
+%%
+%% Description: Returns a list of relevant versions.
+%%--------------------------------------------------------------------
+versions() ->
+ Vsns = ssl_record:supported_protocol_versions(),
+ SupportedVsns = [ssl_record:protocol_version(Vsn) || Vsn <- Vsns],
+ AvailableVsns = ?DEFAULT_SUPPORTED_VERSIONS,
+ [{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}].
+
+%%%--------------------------------------------------------------
+%%% Internal functions
+%%%--------------------------------------------------------------------
+new_connect(Address, Port, Options, Timeout) when is_list(Options) ->
+ try handle_options(Options, client) of
+ {ok, Config} ->
+ do_new_connect(Address,Port,Config,Timeout)
+ catch
+ throw:Error ->
+ Error
+ end.
+
+do_new_connect(Address, Port,
+ #config{cb=CbInfo, inet_user=UserOpts, ssl=SslOpts,
+ emulated=EmOpts,inet_ssl=SocketOpts},
+ Timeout) ->
+ {CbModule, _, _} = CbInfo,
+ try CbModule:connect(Address, Port, SocketOpts, Timeout) of
+ {ok, Socket} ->
+ ssl_connection:connect(Address, Port, Socket, {SslOpts,EmOpts},
+ self(), CbInfo, Timeout);
+ {error, Reason} ->
+ {error, Reason}
+ catch
+ exit:{function_clause, _} ->
+ {error, {eoptions, {cb_info, CbInfo}}};
+ exit:{badarg, _} ->
+ {error,{eoptions, {inet_options, UserOpts}}}
+ end.
+
+old_connect(Address, Port, Options, Timeout) ->
+ ensure_old_ssl_started(),
+ {ok, Pid} = ssl_broker:start_broker(connector),
+ ssl_broker:connect(Pid, Address, Port, Options, Timeout).
+
+new_listen(Port, Options0) ->
+ try
+ {ok, Config} = handle_options(Options0, server),
+ #config{cb={CbModule, _, _},inet_user=Options} = Config,
+ case CbModule:listen(Port, Options) of
+ {ok, ListenSocket} ->
+ {ok, #sslsocket{pid = {ListenSocket, Config}, fd = new_ssl}};
+ Err = {error, _} ->
+ Err
+ end
+ catch
+ Error = {error, _} ->
+ Error
+ end.
+
+old_listen(Port, Options) ->
+ ensure_old_ssl_started(),
+ {ok, Pid} = ssl_broker:start_broker(listener),
+ ssl_broker:listen(Pid, Port, Options).
+
+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,
+
+ {Verify, FailIfNoPeerCert, CaCertDefault} =
+ %% 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)};
+ 1 ->
+ {verify_peer, false, ca_cert_default(verify_peer, Role)};
+ 2 ->
+ {verify_peer, true, ca_cert_default(verify_peer, Role)};
+ verify_none ->
+ {verify_none, false, ca_cert_default(verify_none, Role)};
+ verify_peer ->
+ {verify_peer, proplists:get_value(fail_if_no_peer_cert,
+ Opts, false),
+ ca_cert_default(verify_peer, Role)};
+ Value ->
+ throw({error, {eoptions, {verify, Value}}})
+ 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),
+ fail_if_no_peer_cert = validate_option(fail_if_no_peer_cert,
+ FailIfNoPeerCert),
+ verify_client_once = handle_option(verify_client_once, Opts, false),
+ depth = handle_option(depth, Opts, 1),
+ certfile = CertFile,
+ keyfile = handle_option(keyfile, Opts, CertFile),
+ key = handle_option(key, Opts, undefined),
+ password = handle_option(password, Opts, ""),
+ cacertfile = handle_option(cacertfile, Opts, CaCertDefault),
+ ciphers = handle_option(ciphers, Opts, []),
+ %% Server side option
+ reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
+ reuse_sessions = handle_option(reuse_sessions, Opts, true),
+ debug = handle_option(debug, Opts, [])
+ },
+
+ CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed}),
+ SslOptions = [versions, verify, verify_fun,
+ depth, certfile, keyfile,
+ key, password, cacertfile, ciphers,
+ debug, reuse_session, reuse_sessions, ssl_imp,
+ cd_info],
+
+ SockOpts = lists:foldl(fun(Key, PropList) ->
+ proplists:delete(Key, PropList)
+ end, Opts, SslOptions),
+
+ {SSLsock, Emulated} = emulated_options(SockOpts),
+ {ok, #config{ssl=SSLOptions, emulated=Emulated, inet_ssl=SSLsock,
+ inet_user=SockOpts, cb=CbInfo}}.
+
+handle_option(OptionName, Opts, Default) ->
+ validate_option(OptionName,
+ proplists:get_value(OptionName, Opts, Default)).
+
+
+validate_option(versions, Versions) ->
+ validate_versions(Versions, Versions);
+validate_option(ssl_imp, Value) when Value == new; Value == old ->
+ Value;
+validate_option(verify, Value)
+ when Value == verify_none; Value == verify_peer ->
+ Value;
+validate_option(verify_fun, Value) when is_function(Value) ->
+ Value;
+validate_option(fail_if_no_peer_cert, Value)
+ when Value == true; Value == false ->
+ Value;
+validate_option(verify_client_once, Value)
+ when Value == true; Value == false ->
+ Value;
+validate_option(depth, Value) when is_integer(Value),
+ Value >= 0, Value =< 255->
+ Value;
+validate_option(certfile, Value) when is_list(Value) ->
+ Value;
+validate_option(keyfile, Value) when is_list(Value) ->
+ Value;
+validate_option(key, Value) when Value == undefined;
+ is_tuple(Value) ->
+ %% element(1, Value)=='RSAPrivateKey' ->
+ Value;
+validate_option(password, Value) when 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(ciphers, Value) when is_list(Value) ->
+ Version = ssl_record:highest_protocol_version([]),
+ try cipher_suites(Version, Value)
+ catch
+ exit:_ ->
+ throw({error, {eoptions, {ciphers, Value}}})
+ end;
+validate_option(reuse_session, Value) when is_function(Value) ->
+ Value;
+validate_option(reuse_sessions, Value) when Value == true;
+ Value == false ->
+ Value;
+validate_option(debug, Value) when is_list(Value); Value == true ->
+ Value;
+validate_option(Opt, Value) ->
+ throw({error, {eoptions, {Opt, Value}}}).
+
+validate_versions([], Versions) ->
+ Versions;
+validate_versions([Version | Rest], Versions) when Version == 'tlsv1.1';
+ Version == tlsv1;
+ Version == sslv3 ->
+ validate_versions(Rest, Versions);
+validate_versions(Ver, Versions) ->
+ throw({error, {eoptions, {Ver, {versions, Versions}}}}).
+
+validate_inet_option(mode, Value)
+ when Value =/= list, Value =/= binary ->
+ throw({error, {eoptions, {mode,Value}}});
+validate_inet_option(packet, Value)
+ when not (is_atom(Value) orelse is_integer(Value)) ->
+ throw({error, {eoptions, {packet,Value}}});
+validate_inet_option(packet_size, Value)
+ when not is_integer(Value) ->
+ throw({error, {eoptions, {packet_size,Value}}});
+validate_inet_option(header, Value)
+ when not is_integer(Value) ->
+ throw({error, {eoptions, {header,Value}}});
+validate_inet_option(active, Value)
+ when Value =/= true, Value =/= false, Value =/= once ->
+ throw({error, {eoptions, {active,Value}}});
+validate_inet_option(_, _) ->
+ ok.
+
+ca_cert_default(verify_none, _) ->
+ undefined;
+%% Client may leave verification up to the user
+ca_cert_default(verify_peer, client) ->
+ undefined;
+%% Server that wants to verify_peer must have
+%% some trusted certs.
+ca_cert_default(verify_peer, server) ->
+ "".
+
+emulated_options() ->
+ [mode, packet, active, header, packet_size].
+
+internal_inet_values() ->
+ [{packet_size,0},{packet, 0},{header, 0},{active, false},{mode,binary}].
+ %%[{packet, ssl},{header, 0},{active, false},{mode,binary}].
+
+socket_options(InetValues) ->
+ #socket_options{
+ mode = proplists:get_value(mode, InetValues),
+ header = proplists:get_value(header, InetValues),
+ active = proplists:get_value(active, InetValues),
+ packet = proplists:get_value(packet, InetValues),
+ packet_size = proplists:get_value(packet_size, InetValues)
+ }.
+
+emulated_options(Opts) ->
+ emulated_options(Opts, internal_inet_values(), #socket_options{}).
+
+emulated_options([{mode,Opt}|Opts], Inet, Emulated) ->
+ validate_inet_option(mode,Opt),
+ emulated_options(Opts, Inet, Emulated#socket_options{mode=Opt});
+emulated_options([{header,Opt}|Opts], Inet, Emulated) ->
+ validate_inet_option(header,Opt),
+ emulated_options(Opts, Inet, Emulated#socket_options{header=Opt});
+emulated_options([{active,Opt}|Opts], Inet, Emulated) ->
+ validate_inet_option(active,Opt),
+ emulated_options(Opts, Inet, Emulated#socket_options{active=Opt});
+emulated_options([{packet,Opt}|Opts], Inet, Emulated) ->
+ validate_inet_option(packet,Opt),
+ emulated_options(Opts, Inet, Emulated#socket_options{packet=Opt});
+emulated_options([{packet_size,Opt}|Opts], Inet, Emulated) ->
+ validate_inet_option(packet_size,Opt),
+ emulated_options(Opts, Inet, Emulated#socket_options{packet_size=Opt});
+emulated_options([Opt|Opts], Inet, Emulated) ->
+ emulated_options(Opts, [Opt|Inet], Emulated);
+emulated_options([], Inet,Emulated) ->
+ {Inet, Emulated}.
+
+cipher_suites(Version, []) ->
+ ssl_cipher:suites(Version);
+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),
+ case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported)] of
+ [] ->
+ Supported;
+ Ciphers ->
+ Ciphers
+ end;
+cipher_suites(Version, [Head | _] = Ciphers0) when is_list(Head) ->
+ %% Format: ["RC4-SHA","RC4-MD5"]
+ Ciphers = [ssl_cipher:openssl_suite(C) || C <- Ciphers0],
+ cipher_suites(Version, Ciphers);
+cipher_suites(Version, Ciphers0) ->
+ %% Format: "RC4-SHA:RC4-MD5"
+ 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]).
+
+%% Start old ssl port program if needed.
+ensure_old_ssl_started() ->
+ case whereis(ssl_server) of
+ undefined ->
+ (catch supervisor:start_child(ssl_sup,
+ {ssl_server, {ssl_server, start_link, []},
+ permanent, 2000, worker, [ssl_server]}));
+ _ ->
+ ok
+ end.
+
+%%%%%%%%%%%%%%%% Deprecated %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ciphers() ->
+ ensure_old_ssl_started(),
+ case (catch ssl_server:ciphers()) of
+ {'EXIT', _} ->
+ {error, enotstarted};
+ Res = {ok, _} ->
+ Res
+ end.
+
+version() ->
+ ensure_old_ssl_started(),
+ SSLVsn = ?VSN,
+ {CompVsn, LibVsn} = case (catch ssl_server:version()) of
+ {'EXIT', _} ->
+ {"", ""};
+ {ok, Vsns} ->
+ Vsns
+ end,
+ {ok, {SSLVsn, CompVsn, LibVsn}}.
+
+%% Only used to remove exit messages from old ssl
+%% First is a nonsense clause to provide some
+%% backward compability for orber that uses this
+%% function in a none recommended way, but will
+%% work correctly if a valid pid is returned.
+pid(#sslsocket{fd = new_ssl}) ->
+ whereis(ssl_connection_sup);
+pid(#sslsocket{pid = Pid}) ->
+ Pid.
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
new file mode 100644
index 0000000000..d3f9c833f1
--- /dev/null
+++ b/lib/ssl/src/ssl_alert.erl
@@ -0,0 +1,107 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Handles an ssl connection, e.i. both the setup
+%% e.i. SSL-Handshake, SSL-Alert and SSL-Cipher protocols and delivering
+%% data to the application. All data on the connectinon is received and
+%% sent according to the SSL-record protocol.
+%% %%----------------------------------------------------------------------
+
+-module(ssl_alert).
+
+-include("ssl_alert.hrl").
+-include("ssl_record.hrl").
+
+-export([alert_txt/1, reason_code/2]).
+
+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).
+
+alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}}) ->
+ Mod ++ ":" ++ integer_to_list(Line) ++ ":" ++
+ level_txt(Level) ++" "++ description_txt(Description).
+
+level_txt(?WARNING) ->
+ "Warning:";
+level_txt(?FATAL) ->
+ "Fatal error:".
+
+description_txt(?CLOSE_NOTIFY) ->
+ "close_notify";
+description_txt(?UNEXPECTED_MESSAGE) ->
+ "unexpected_message";
+description_txt(?BAD_RECORD_MAC) ->
+ "bad_record_mac";
+description_txt(?DECRYPTION_FAILED) ->
+ "decryption_failed";
+description_txt(?RECORD_OVERFLOW) ->
+ "record_overflow";
+description_txt(?DECOMPRESSION_FAILURE) ->
+ "decompression_failure";
+description_txt(?HANDSHAKE_FAILURE) ->
+ "handshake_failure";
+description_txt(?BAD_CERTIFICATE) ->
+ "bad_certificate";
+description_txt(?UNSUPPORTED_CERTIFICATE) ->
+ "unsupported_certificate";
+description_txt(?CERTIFICATE_REVOKED) ->
+ "certificate_revoked";
+description_txt(?CERTIFICATE_EXPIRED) ->
+ "certificate_expired";
+description_txt(?CERTIFICATE_UNKNOWN) ->
+ "certificate_unknown";
+description_txt(?ILLEGAL_PARAMETER) ->
+ "illegal_parameter";
+description_txt(?UNKNOWN_CA) ->
+ "unknown_ca";
+description_txt(?ACCESS_DENIED) ->
+ "access_denied";
+description_txt(?DECODE_ERROR) ->
+ "decode_error";
+description_txt(?DECRYPT_ERROR) ->
+ "decrypt_error";
+description_txt(?EXPORT_RESTRICTION) ->
+ "export_restriction";
+description_txt(?PROTOCOL_VERSION) ->
+ "protocol_version";
+description_txt(?INSUFFICIENT_SECURITY) ->
+ "insufficient_security";
+description_txt(?INTERNAL_ERROR) ->
+ "internal_error";
+description_txt(?USER_CANCELED) ->
+ "user_canceled";
+description_txt(?NO_RENEGOTIATION) ->
+ "no_renegotiation".
+
+
+
+
+
diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl
new file mode 100644
index 0000000000..6470b82d50
--- /dev/null
+++ b/lib/ssl/src/ssl_alert.hrl
@@ -0,0 +1,97 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Record and constant defenitions for the SSL-alert protocol
+%% see RFC 2246
+%%----------------------------------------------------------------------
+
+-ifndef(ssl_alert).
+-define(ssl_alert, true).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Alert protocol - RFC 2246 section 7.2
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% AlertLevel
+-define(WARNING, 1).
+-define(FATAL, 2).
+
+%% {AlertDescription
+%% enum {
+%% close_notify(0),
+%% unexpected_message(10),
+%% bad_record_mac(20),
+%% decryption_failed(21),
+%% record_overflow(22),
+%% decompression_failure(30),
+%% handshake_failure(40),
+%% bad_certificate(42),
+%% unsupported_certificate(43),
+%% certificate_revoked(44),
+%% certificate_expired(45),
+ %% certificate_unknown(46),
+%% illegal_parameter(47),
+%% unknown_ca(48),
+%% access_denied(49),
+%% decode_error(50),
+%% decrypt_error(51),
+%% export_restriction(60),
+%% protocol_version(70),
+%% insufficient_security(71),
+%% internal_error(80),
+%% user_canceled(90),
+%% no_renegotiation(100),
+%% (255)
+%% } AlertDescription;
+
+-define(CLOSE_NOTIFY, 0).
+-define(UNEXPECTED_MESSAGE, 10).
+-define(BAD_RECORD_MAC, 20).
+-define(DECRYPTION_FAILED, 21).
+-define(RECORD_OVERFLOW, 22).
+-define(DECOMPRESSION_FAILURE, 30).
+-define(HANDSHAKE_FAILURE, 40).
+-define(BAD_CERTIFICATE, 42).
+-define(UNSUPPORTED_CERTIFICATE, 43).
+-define(CERTIFICATE_REVOKED, 44).
+-define(CERTIFICATE_EXPIRED, 45).
+-define(CERTIFICATE_UNKNOWN, 46).
+-define(ILLEGAL_PARAMETER, 47).
+-define(UNKNOWN_CA, 48).
+-define(ACCESS_DENIED, 49).
+-define(DECODE_ERROR, 50).
+-define(DECRYPT_ERROR, 51).
+-define(EXPORT_RESTRICTION, 60).
+-define(PROTOCOL_VERSION, 70).
+-define(INSUFFICIENT_SECURITY, 71).
+-define(INTERNAL_ERROR, 80).
+-define(USER_CANCELED, 90).
+-define(NO_RENEGOTIATION, 100).
+
+-define(ALERT_REC(Level,Desc), #alert{level=Level,description=Desc,where={?FILE, ?LINE}}).
+
+%% Alert
+-record(alert, {
+ level,
+ description,
+ where = {?FILE, ?LINE}
+ }).
+-endif. % -ifdef(ssl_alert).
diff --git a/lib/ssl/src/ssl_app.erl b/lib/ssl/src/ssl_app.erl
new file mode 100644
index 0000000000..6ca1c42631
--- /dev/null
+++ b/lib/ssl/src/ssl_app.erl
@@ -0,0 +1,41 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-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 : Application master for SSL.
+
+-module(ssl_app).
+
+-behaviour(application).
+
+-export([start/2, stop/1]).
+
+%% start/2(Type, StartArgs) -> {ok, Pid} | {ok, Pid, State} |
+%% {error, Reason}
+%%
+start(_Type, _StartArgs) ->
+ ssl_sup:start_link().
+
+%% stop(State) -> void()
+%%
+stop(_State) ->
+ ok.
+
+
diff --git a/lib/ssl/src/ssl_base64.erl b/lib/ssl/src/ssl_base64.erl
new file mode 100644
index 0000000000..cfc42407e8
--- /dev/null
+++ b/lib/ssl/src/ssl_base64.erl
@@ -0,0 +1,129 @@
+%%
+%% %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_broker.erl b/lib/ssl/src/ssl_broker.erl
new file mode 100644
index 0000000000..178fb5fcb9
--- /dev/null
+++ b/lib/ssl/src/ssl_broker.erl
@@ -0,0 +1,1188 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-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 : SSL broker
+
+-module(ssl_broker).
+-behaviour(gen_server).
+
+%% This module implements brokers for ssl. A broker is either a connector,
+%% an acceptor, or a listener. All brokers are children to ssl_broker_sup,
+%% to which they are linked. Each broker is also linked to ssl_server, and
+%% to its client.
+%%
+%% The purpose of the broker is to set up SSL connections through calls to
+%% ssl_server and gen_tcp. All control information goes to the server,
+%% while all data is exchanged directly between gen_tcp and the port program
+%% of the ssl_server.
+%%
+%% A broker is created by a call to start_broker/3 (do *not* use start_link/4
+%% - it is for ssl_broker_sup to call that one), and then call listen/3,
+%% accept/4, or connect/5.
+%%
+%% The following table shows all functions dependency on status, active
+%% mode etc.
+%%
+%% Permitted status transitions:
+%%
+%% nil -> open
+%% open -> closing | closed (termination)
+%% closing -> closed (termination)
+%%
+%% We are rather sloppy about nil, and consider open/closing == !closed,
+%% open/closing/closed === any etc.
+%%
+%%
+%% function/ valid mode new
+%% message status state
+%%
+%% calls
+%% -----
+%% recv open passive ditto
+%% send open any ditto
+%% transport_accept nil any open
+%% ssl_accept nil any open
+%% connect nil any open
+%% listen nil any open
+%% peername open/closing any ditto
+%% setopts open/closing any ditto
+%% getopts open/closing any ditto
+%% sockname open/closing any ditto
+%% peercert open/closing any ditto
+%% inhibit any any ditto
+%% release any any ditto
+%% close any any closed (1)
+%%
+%% info
+%% ----
+%% tcp open active ditto
+%% tcp_closed open | closing active closing
+%% tcp_error open | closing active closing
+%%
+%% (1) We just terminate.
+%%
+%% TODO
+%%
+%% XXX Timeouts are not checked (integer or infinity).
+%%
+%% XXX The collector thing is not gen_server compliant.
+%%
+%% NOTE: There are three different "modes": (a) passive or active mode,
+%% specified as {active, bool()}, and (b) list or binary mode, specified
+%% as {mode, list | binary}, and (c) encrypted or clear mode
+%%
+
+-include("ssl_int.hrl").
+
+%% External exports
+
+-export([start_broker/1, start_broker/2, start_link/3,
+ transport_accept/3, ssl_accept/2,
+ close/1, connect/5, connection_info/1, controlling_process/2,
+ listen/3, recv/3, send/2, getopts/2, getopts/3, setopts/2,
+ sockname/1, peername/1, peercert/1]).
+
+-export([listen_prim/5, connect_prim/8,
+ transport_accept_prim/5, ssl_accept_prim/6]).
+
+%% Internal exports
+
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ code_change/3, terminate/2, collector_init/1]).
+
+-include("ssl_broker_int.hrl").
+
+%% start_broker(Type) -> {ok, Pid} | {error, Reason}
+%% start_broker(Type, GenOpts) -> {ok, Pid} | {error, Reason}
+%% Type = accept | connect | listen
+%% GenOpts = /standard gen_server options/
+%%
+%% This is the function to be called from the interface module ssl.erl.
+%% Links to the caller.
+%%
+start_broker(Type) ->
+ start_broker(Type, []).
+
+start_broker(Type, GenOpts) ->
+ case lists:member(Type, [listener, acceptor, connector]) of
+ true ->
+ case supervisor:start_child(ssl_broker_sup,
+ [self(), Type, GenOpts]) of
+ {ok, Pid} ->
+ link(Pid),
+ {ok, Pid};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ false ->
+ {error, ebrokertype}
+ end.
+
+%% start_link(Client, Type, GenOpts) -> {ok, Pid} | {error, Reason}
+%%
+%% Type = accept | connect | listen
+%% GenOpts = /standard gen_server options/
+%%
+%% This function is called by ssl_broker_sup and must *not* be called
+%% from an interface module (ssl.erl).
+
+start_link(Client, Type, GenOpts) ->
+ gen_server:start_link(?MODULE, [Client, Type], GenOpts).
+
+
+%% accept(Pid, ListenSocket, Timeout) -> {ok, Socket} | {error, Reason}
+%%
+%% Types: Pid = pid() of acceptor
+%% ListenSocket = Socket = sslsocket()
+%% Timeout = timeout()
+%%
+%% accept(Pid, ListenSocket, Timeout)
+%% when is_pid(Pid), is_record(ListenSocket, sslsocket) ->
+%% Req = {accept, self(), ListenSocket, Timeout},
+%% gen_server:call(Pid, Req, infinity).
+
+%% transport_accept(Pid, ListenSocket, Timeout) -> {ok, Socket} |
+%% {error, Reason}
+%%
+%% Types: Pid = pid() of acceptor
+%% ListenSocket = Socket = sslsocket()
+%% Timeout = timeout()
+%%
+transport_accept(Pid, #sslsocket{} = ListenSocket, Timeout) when is_pid(Pid) ->
+ Req = {transport_accept, self(), ListenSocket, Timeout},
+ gen_server:call(Pid, Req, infinity).
+
+%% ssl_accept(Pid, Socket, Timeout) -> {ok, Socket} | {error, Reason}
+%%
+%% Types: Pid = pid() of acceptor
+%% ListenSocket = Socket = sslsocket()
+%% Timeout = timeout()
+%%
+ssl_accept(#sslsocket{pid = Pid} = Socket, Timeout) ->
+ Req = {ssl_accept, self(), Socket, Timeout},
+ gen_server:call(Pid, Req, infinity).
+
+%% close(Socket) -> ok | {error, Reason}
+%%
+%% Types: Socket = sslsocket() | pid()
+%%
+close(#sslsocket{pid = Pid}) ->
+ close(Pid);
+close(Pid) when is_pid(Pid) ->
+ gen_server:call(Pid, {close, self()}, infinity).
+
+%% connect(Pid, Address, Port, Opts, Timeout) -> {ok, Socket} | {error, Reason}
+%%
+%% Types: Pid = pid() of connector
+%% Address = string() | {byte(), byte(), byte(), byte()}
+%% Port = int()
+%% Opts = options()
+%% Timeout = timeout()
+%% Socket = sslsocket()
+%%
+connect(Pid, Address, Port, Opts, Timeout) when is_pid(Pid), is_list(Opts) ->
+ case are_connect_opts(Opts) of
+ true ->
+ Req = {connect, self(), Address, Port, Opts, Timeout},
+ gen_server:call(Pid, Req, infinity);
+ false ->
+ {error, eoptions}
+ end.
+
+%%
+%% connection_info(Socket) -> {ok, {Protocol, Cipher} | {error, Reason}
+%%
+connection_info(#sslsocket{pid = Pid}) ->
+ Req = {connection_info, self()},
+ gen_server:call(Pid, Req, infinity).
+
+%% controlling_process(Socket, NewOwner) -> ok | {error, Reason}
+
+controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(NewOwner) ->
+ case gen_server:call(Pid, {inhibit_msgs, self()}, infinity) of
+ ok ->
+ transfer_messages(Pid, NewOwner),
+ gen_server:call(Pid, {release_msgs, self(), NewOwner}, infinity);
+ Error ->
+ Error
+ end.
+
+%% listen(Pid, Port, Opts) -> {ok, ListenSocket} | {error, Reason}
+%%
+%% Types: Pid = pid() of listener
+%% Port = int()
+%% Opts = options()
+%% ListenSocket = sslsocket()
+%%
+listen(Pid, Port, Opts) when is_pid(Pid) ->
+ case are_listen_opts(Opts) of
+ true ->
+ Req = {listen, self(), Port, Opts},
+ gen_server:call(Pid, Req, infinity);
+ false ->
+ {error, eoptions}
+ end.
+
+
+%%
+%% peername(Socket) -> {ok, {Address, Port}} | {error, Reason}
+%%
+peername(#sslsocket{pid = Pid}) ->
+ Req = {peername, self()},
+ gen_server:call(Pid, Req, infinity).
+
+
+%% recv(Socket, Length, Timeout) -> {ok, Data} | {error, Reason}
+%%
+%% Types: Socket = sslsocket()
+%% Length = Timeout = integer()
+%% Data = bytes() | binary()
+%%
+recv(#sslsocket{pid = Pid}, Length, Timeout) ->
+ Req = {recv, self(), Length, Timeout},
+ gen_server:call(Pid, Req, infinity).
+
+
+%% send(Socket, Data) -> ok | {error, Reason}
+%%
+%% Types: Socket = sslsocket()
+%%
+send(#sslsocket{pid = Pid}, Data) ->
+ gen_server:call(Pid, {send, self(), Data}, infinity).
+
+
+%% getopts(Socket, OptTags) -> {ok, Opts} | {error, einval}
+%%
+%% Types: Pid = pid() of broker
+%% Timeout = timeout()
+%% OptTags = option_tags()
+%% Opts = options()
+%%
+getopts(Socket, OptTags) ->
+ getopts(Socket, OptTags, infinity).
+
+getopts(#sslsocket{pid = Pid}, OptTags, Timeout) when is_list(OptTags) ->
+ Req = {getopts, self(), OptTags},
+ gen_server:call(Pid, Req, Timeout).
+
+
+%%
+%% setopts(Socket, Opts) -> ok | {error, Reason}
+%%
+setopts(#sslsocket{pid = Pid}, Opts) ->
+ Req = {setopts, self(), Opts},
+ gen_server:call(Pid, Req, infinity).
+
+%%
+%% sockname(Socket) -> {ok, {Address, Port}} | {error, Reason}
+%%
+sockname(#sslsocket{pid = Pid}) ->
+ Req = {sockname, self()},
+ gen_server:call(Pid, Req, infinity).
+
+
+%%
+%% peercert(Socket) -> {ok, Cert} | {error, Reason}
+%%
+peercert(#sslsocket{pid = Pid}) ->
+ Req = {peercert, self()},
+ gen_server:call(Pid, Req, infinity).
+
+%%
+%% INIT
+%%
+
+%% init
+%%
+init([Client, Type]) ->
+ process_flag(trap_exit, true),
+ link(Client),
+ Debug = case application:get_env(ssl, edebug) of
+ {ok, true} ->
+ true;
+ _ ->
+ case application:get_env(ssl, debug) of
+ {ok, true} ->
+ true;
+ _ ->
+ os:getenv("ERL_SSL_DEBUG") =/= false
+ end
+ end,
+ Server = whereis(ssl_server),
+ if
+ is_pid(Server) ->
+ link(Server),
+ debug1(Debug, Type, "in start, client = ~w", [Client]),
+ {ok, #st{brokertype = Type, server = Server, client = Client,
+ collector = Client, debug = Debug}};
+ true ->
+ {stop, no_ssl_server}
+ end.
+
+
+%%
+%% HANDLE CALL
+%%
+
+%% recv - passive mode
+%%
+handle_call({recv, Client, Length, Timeout}, _From,
+ #st{active = false, proxysock = Proxysock, status = Status} = St) ->
+ debug(St, "recv: client = ~w~n", [Client]),
+ if
+ Status =/= open ->
+ {reply, {error, closed}, St};
+ true ->
+ case gen_tcp:recv(Proxysock, Length, Timeout) of
+ {ok, Data} ->
+ {reply, {ok, Data}, St};
+ {error, timeout} ->
+ {reply, {error, timeout}, St};
+ {error, Reason} ->
+ {reply, {error, Reason}, St#st{status = closing}}
+ end
+ end;
+
+%% send
+%%
+handle_call({send, Client, Data}, _From, St) ->
+ debug(St, "send: client = ~w~n", [Client]),
+ if
+ St#st.status =/= open ->
+ {reply, {error, closed}, St};
+ true ->
+ case gen_tcp:send(St#st.proxysock, Data) of
+ ok ->
+ {reply, ok, St};
+ {error, _Reason} ->
+ {reply, {error, closed}, St#st{status = closing}}
+ end
+ end;
+
+%% transport_accept
+%%
+%% Client = pid of client
+%% ListenSocket = sslsocket()
+%%
+handle_call({transport_accept, Client, ListenSocket, Timeout}, _From, St) ->
+ debug(St, "transport_accept: client = ~w, listensocket = ~w~n",
+ [Client, ListenSocket]),
+ case getopts(ListenSocket, tcp_listen_opt_tags(), ?DEF_TIMEOUT) of
+ {ok, LOpts} ->
+ case transport_accept_prim(
+ ssl_server, ListenSocket#sslsocket.fd, LOpts, Timeout, St) of
+ {ok, ThisSocket, NSt} ->
+ {reply, {ok, ThisSocket}, NSt};
+ {error, Reason, St} ->
+ What = what(Reason),
+ {stop, normal, {error, What}, St}
+ end;
+ {error, Reason} ->
+ What = what(Reason),
+ {stop, normal, {error, What}, St}
+ end;
+
+%% ssl_accept
+%%
+%% Client = pid of client
+%% ListenSocket = sslsocket()
+%%
+handle_call({ssl_accept, Client, Socket, Timeout}, _From, St) ->
+ debug(St, "ssl_accept: client = ~w, socket = ~w~n", [Client, Socket]),
+ case ssl_accept_prim(ssl_server, gen_tcp, Client, St#st.opts, Timeout, St#st{thissock=Socket}) of
+ {ok, Socket, NSt} ->
+ {reply, ok, NSt};
+ {error, Reason, St} ->
+ What = what(Reason),
+ {stop, normal, {error, What}, St}
+ end;
+
+%% connect
+%%
+%% Client = client pid
+%% Address = hostname | ipstring | IP
+%% Port = integer()
+%% Opts = options()
+%%
+handle_call({connect, Client, Address, Port, Opts, Timeout}, _From, St) ->
+ debug(St, "connect: client = ~w, address = ~p, port = ~w~n",
+ [Client, Address, Port]),
+ case connect_prim(ssl_server, gen_tcp, Client, Address, Port, Opts,
+ Timeout, St) of
+ {ok, Res, NSt} ->
+ {reply, {ok, Res}, NSt};
+ {error, Reason, NSt} ->
+ What = what(Reason),
+ {stop, normal, {error, What}, NSt}
+ end;
+
+%% connection_info
+%%
+handle_call({connection_info, Client}, _From, St) ->
+ debug(St, "connection_info: client = ~w~n", [Client]),
+ Reply = ssl_server:connection_info(St#st.fd),
+ {reply, Reply, St};
+
+%% close from client
+%%
+handle_call({close, Client}, _From, St) ->
+ debug(St, "close: client = ~w~n", [Client]),
+ %% Terminate
+ {stop, normal, ok, St#st{status = closed}};
+
+%% listen
+%%
+%% Client = pid of client
+%% Port = int()
+%% Opts = options()
+%%
+handle_call({listen, Client, Port, Opts}, _From, St) ->
+ debug(St, "listen: client = ~w, port = ~w~n",
+ [Client, Port]),
+ case listen_prim(ssl_server, Client, Port, Opts, St) of
+ {ok, Res, NSt} ->
+ {reply, {ok, Res}, NSt};
+ {error, Reason, NSt} ->
+ What = what(Reason),
+ {stop, normal, {error, What}, NSt}
+ end;
+
+%% peername
+%%
+handle_call({peername, Client}, _From, St) ->
+ debug(St, "peername: client = ~w~n", [Client]),
+ Reply = case ssl_server:peername(St#st.fd) of
+ {ok, {Address, Port}} ->
+ {ok, At} = inet_parse:ipv4_address(Address),
+ {ok, {At, Port}};
+ Error ->
+ Error
+ end,
+ {reply, Reply, St};
+
+%% setopts
+%%
+handle_call({setopts, Client, Opts0}, _From, St0) ->
+ debug(St0, "setopts: client = ~w~n", [Client]),
+ OptsOK = case St0#st.brokertype of
+ listener ->
+ are_opts(fun is_tcp_listen_opt/1, Opts0);
+ acceptor ->
+ are_opts(fun is_tcp_accept_opt/1, Opts0);
+ connector ->
+ are_opts(fun is_tcp_connect_opt/1, Opts0)
+ end,
+ if
+ OptsOK =:= false ->
+ {reply, {error, eoptions}, St0};
+ true ->
+ Opts1 = lists:keydelete(nodelay, 1, Opts0),
+ case inet:setopts(St0#st.proxysock, Opts1) of
+ ok ->
+ Opts2 = replace_opts(Opts1, St0#st.opts),
+ Active = get_active(Opts2),
+ St2 = St0#st{opts = Opts2,
+ active = Active},
+ case get_nodelay(Opts0) of
+ empty ->
+ {reply, ok, St2};
+ Bool ->
+ case setnodelay(ssl_server, St0, Bool) of
+ ok ->
+ Opts3 = replace_opts([{nodelay, Bool}],
+ Opts2),
+ St3 = St0#st{opts = Opts3,
+ active = Active},
+ {reply, ok, St3};
+ {error, Reason} ->
+ {reply, {error, Reason}, St2}
+ end
+ end;
+ {error, Reason} ->
+ {reply, {error, Reason}, St0}
+ end
+ end;
+
+%% sockname
+%%
+handle_call({sockname, Client}, _From, St) ->
+ debug(St, "sockname: client = ~w~n", [Client]),
+ Reply = case ssl_server:sockname(St#st.fd) of
+ {ok, {Address, Port}} ->
+ {ok, At} = inet_parse:ipv4_address(Address),
+ {ok, {At, Port}};
+ Error ->
+ Error
+ end,
+ {reply, Reply, St};
+
+%% peercert
+%%
+handle_call({peercert, Client}, _From, St) ->
+ debug(St, "peercert: client = ~w~n", [Client]),
+ Reply = ssl_server:peercert(St#st.fd),
+ {reply, Reply, St};
+
+%% inhibit msgs
+%%
+handle_call({inhibit_msgs, Client}, _From, #st{client = Client} = St) ->
+ debug(St, "inhibit_msgs: client = ~w~n", [Client]),
+ {ok, Collector} = start_collector(),
+ {reply, ok, St#st{collector = Collector}};
+
+%% release msgs
+%%
+handle_call({release_msgs, Client, NewClient}, _From,
+ #st{client = Client, collector = Collector} = St) ->
+ debug(St, "release_msgs: client = ~w~n", [Client]),
+ unlink(Client),
+ link(NewClient),
+ release_collector(Collector, NewClient),
+ NSt = St#st{client = NewClient, collector = NewClient},
+ {reply, ok, NSt};
+
+%% getopts
+%%
+handle_call({getopts, Client, OptTags}, _From, St) ->
+ debug(St, "getopts: client = ~w~n", [Client]),
+ Reply = case are_opt_tags(St#st.brokertype, OptTags) of
+ true ->
+ {ok, extract_opts(OptTags, St#st.opts)};
+ _ ->
+ {error, einval}
+ end,
+ {reply, Reply, St};
+
+%% bad call
+%%
+handle_call(Request, _From, St) ->
+ debug(St, "++++ ssl_broker: bad call: ~w~n", [Request]),
+ {reply, {error, {badcall, Request}}, St}.
+
+%%
+%% HANDLE CAST
+%%
+
+handle_cast(Request, St) ->
+ debug(St, "++++ ssl_broker: bad cast: ~w~n", [Request]),
+ {stop, {error, {badcast, Request}}, St}.
+
+%%
+%% HANDLE INFO
+%%
+
+%% tcp - active mode
+%%
+%% The collector is different from client only during change of
+%% controlling process.
+%%
+handle_info({tcp, Socket, Data},
+ #st{active = Active, collector = Collector, status = open,
+ proxysock = Socket, thissock = Thissock} = St)
+ when Active =/= false ->
+ debug(St, "tcp: socket = ~w~n", [Socket]),
+ Msg = {ssl, Thissock, Data},
+ Collector ! Msg,
+ if
+ Active =:= once ->
+ {noreply, St#st{active = false}};
+ true ->
+ {noreply, St}
+ end;
+
+%% tcp_closed - from proxy socket, active mode
+%%
+%%
+handle_info({tcp_closed, Socket},
+ #st{active = Active, collector = Collector,
+ proxysock = Socket, thissock = Thissock} = St)
+ when Active =/= false ->
+ debug(St, "tcp_closed: socket = ~w~n", [Socket]),
+ Msg = {ssl_closed, Thissock},
+ Collector ! Msg,
+ if
+ Active =:= once ->
+ {noreply, St#st{status = closing, active = false}};
+ true ->
+ {noreply, St#st{status = closing}}
+ end;
+
+%% tcp_error - from proxy socket, active mode
+%%
+%%
+handle_info({tcp_error, Socket, Reason},
+ #st{active = Active, collector = Collector,
+ proxysock = Socket} = St)
+ when Active =/= false ->
+ debug(St, "tcp_error: socket = ~w, reason = ~w~n", [Socket, Reason]),
+ Msg = {ssl_error, Socket, Reason},
+ Collector ! Msg,
+ if
+ Active =:= once ->
+ {noreply, St#st{status = closing, active = false}};
+ true ->
+ {noreply, St#st{status = closing}}
+ end;
+
+%% EXIT - from client
+%%
+%%
+handle_info({'EXIT', Client, Reason}, #st{client = Client} = St) ->
+ debug(St, "exit client: client = ~w, reason = ~w~n", [Client, Reason]),
+ {stop, normal, St#st{status = closed}}; % do not make noise
+
+%% EXIT - from server
+%%
+%%
+handle_info({'EXIT', Server, Reason}, #st{server = Server} = St) ->
+ debug(St, "exit server: reason = ~w~n", [Reason]),
+ {stop, Reason, St};
+
+%% handle info catch all
+%%
+handle_info(Info, St) ->
+ debug(St, " bad info: ~w~n", [Info]),
+ {stop, {error, {badinfo, Info}}, St}.
+
+
+%% terminate
+%%
+%%
+terminate(Reason, St) ->
+ debug(St, "in terminate reason: ~w, state: ~w~n", [Reason, St]),
+ ok.
+
+%% code_change
+%%
+%%
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%
+%% Primitive interface
+%%
+listen_prim(ServerName, Client, Port, Opts, St) ->
+ LOpts = get_tcp_listen_opts(Opts),
+ SSLOpts = get_ssl_opts(Opts),
+ FlagStr =mk_ssl_optstr(SSLOpts),
+ BackLog = get_backlog(LOpts),
+ IP = get_ip(LOpts),
+ case ssl_server:listen_prim(ServerName, IP, Port, FlagStr, BackLog) of
+ {ok, ListenFd, _Port0} ->
+ ThisSocket = #sslsocket{fd = ListenFd, pid = self()},
+ StOpts = add_default_tcp_listen_opts(LOpts) ++
+ add_default_ssl_opts(SSLOpts),
+ NSt = St#st{fd = ListenFd,
+ active = get_active(LOpts), % irrelevant for listen
+ opts = StOpts,
+ thissock = ThisSocket,
+ status = open},
+ debug(St, "listen: ok: client = ~w, listenfd = ~w~n",
+ [Client, ListenFd]),
+ {ok, ThisSocket, NSt};
+ {error, Reason} ->
+ {error, Reason, St}
+ end.
+
+connect_prim(ServerName, TcpModule, Client, FAddress, FPort, Opts,
+ Timeout, St) ->
+ COpts = get_tcp_connect_opts(Opts),
+ SSLOpts = get_ssl_opts(Opts),
+ FlagStr = mk_ssl_optstr(SSLOpts),
+ case inet:getaddr(FAddress, inet) of
+ {ok, FIP} ->
+ %% Timeout is gen_server timeout - hence catch
+ LIP = get_ip(COpts),
+ LPort = get_port(COpts),
+ case (catch ssl_server:connect_prim(ServerName,
+ LIP, LPort, FIP, FPort,
+ FlagStr, Timeout)) of
+ {ok, Fd, ProxyPort} ->
+ case connect_proxy(ServerName, TcpModule, Fd,
+ ProxyPort, COpts, Timeout) of
+ {ok, Socket} ->
+ ThisSocket = #sslsocket{fd = Fd, pid = self()},
+ StOpts = add_default_tcp_connect_opts(COpts) ++
+ add_default_ssl_opts(SSLOpts),
+ NSt = St#st{fd = Fd,
+ active = get_active(COpts),
+ opts = StOpts,
+ thissock = ThisSocket,
+ proxysock = Socket,
+ status = open},
+ case get_nodelay(COpts) of
+ true -> setnodelay(ServerName, NSt, true);
+ _ -> ok
+ end,
+ debug(St, "connect: ok: client = ~w, fd = ~w~n",
+ [Client, Fd]),
+ {ok, ThisSocket, NSt};
+ {error, Reason} ->
+ {error, Reason, St}
+ end;
+ {'EXIT', Reason} ->
+ {error, Reason, St};
+ {error, Reason} ->
+ {error, Reason, St}
+ end;
+ {error, Reason} ->
+ {error, Reason, St}
+ end.
+
+transport_accept_prim(ServerName, ListenFd, LOpts, Timeout, St) ->
+ AOpts = get_tcp_accept_opts(LOpts),
+ FlagStr = "",
+ %% Timeout is gen_server timeout - hence catch.
+ case (catch ssl_server:transport_accept_prim(ServerName, ListenFd,
+ FlagStr, Timeout)) of
+ {ok, Fd, ProxyPort} ->
+ ThisSocket = #sslsocket{fd = Fd, pid = self()},
+ NSt = St#st{fd = Fd,
+ active = get_active(AOpts),
+ opts = AOpts,
+ thissock = ThisSocket,
+ proxyport = ProxyPort,
+ encrypted = false},
+ debug(St, "transport_accept: ok: fd = ~w~n", [Fd]),
+ {ok, ThisSocket, NSt};
+ {'EXIT', Reason} ->
+ debug(St, "transport_accept: EXIT: Reason = ~w~n", [Reason]),
+ {error, Reason, St};
+ {error, Reason} ->
+ debug(St, "transport_accept: error: Reason = ~w~n", [Reason]),
+ {error, Reason, St}
+ end.
+
+ssl_accept_prim(ServerName, TcpModule, Client, LOpts, Timeout, St) ->
+ FlagStr = [],
+ SSLOpts = [],
+ AOpts = get_tcp_accept_opts(LOpts),
+ %% Timeout is gen_server timeout - hence catch.
+ debug(St, "ssl_accept_prim: self() ~w Client ~w~n", [self(), Client]),
+ Socket = St#st.thissock,
+ Fd = Socket#sslsocket.fd,
+ A = (catch ssl_server:ssl_accept_prim(ServerName, Fd, FlagStr, Timeout)),
+ debug(St, "ssl_accept_prim: ~w~n", [A]),
+ case A of
+ ok ->
+ B = connect_proxy(ServerName, TcpModule, Fd,
+ St#st.proxyport, AOpts, Timeout),
+ debug(St, "ssl_accept_prim: connect_proxy ~w~n", [B]),
+ case B of
+ {ok, Socket2} ->
+ StOpts = add_default_tcp_accept_opts(AOpts) ++
+ add_default_ssl_opts(SSLOpts),
+ NSt = St#st{opts = StOpts,
+ proxysock = Socket2,
+ encrypted = true,
+ status = open},
+ case get_nodelay(AOpts) of
+ true -> setnodelay(ServerName, NSt, true);
+ _ -> ok
+ end,
+ debug(St, "transport_accept: ok: client = ~w, fd = ~w~n",
+ [Client, Fd]),
+ {ok, St#st.thissock, NSt};
+ {error, Reason} ->
+ {error, Reason, St}
+ end;
+ {'EXIT', Reason} ->
+ {error, Reason, St};
+ {error, Reason} ->
+ {error, Reason, St}
+ end.
+
+
+%%
+%% LOCAL FUNCTIONS
+%%
+
+%%
+%% connect_proxy(Fd, ProxyPort, TOpts, Timeout) -> {ok, Socket} |
+%% {error, Reason}
+%%
+connect_proxy(ServerName, TcpModule, Fd, ProxyPort, TOpts, Timeout) ->
+ case TcpModule:connect({127, 0, 0, 1}, ProxyPort, TOpts, Timeout) of
+ {ok, Socket} ->
+ {ok, Port} = inet:port(Socket),
+ A = ssl_server:proxy_join_prim(ServerName, Fd, Port),
+ case A of
+ ok ->
+ {ok, Socket};
+ Error ->
+ Error
+ end;
+ Error ->
+ Error
+ end.
+
+
+setnodelay(ServerName, St, Bool) ->
+ case ssl_server:setnodelay_prim(ServerName, St#st.fd, Bool) of
+ ok ->
+ case inet:setopts(St#st.proxysock, [{nodelay, Bool}]) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%
+%% start_collector()
+%%
+%% A collector is a little process that keeps messages during change of
+%% controlling process.
+%% XXX This is not gen_server compliant :-(.
+%%
+start_collector() ->
+ Pid = spawn_link(?MODULE, collector_init, [self()]),
+ {ok, Pid}.
+
+%%
+%% release_collector(Collector, NewOwner)
+%%
+release_collector(Collector, NewOwner) ->
+ Collector ! {release, self(), NewOwner},
+ receive
+ %% Reap collector
+ {'EXIT', Collector, normal} ->
+ ok
+ end.
+
+%%
+%% collector_init(Broker) -> void()
+%%
+collector_init(Broker) ->
+ receive
+ {release, Broker, NewOwner} ->
+ transfer_messages(Broker, NewOwner)
+ end.
+
+%%
+%% transfer_messages(Pid, NewOwner) -> void()
+%%
+transfer_messages(Pid, NewOwner) ->
+ receive
+ {ssl, Sock, Data} ->
+ NewOwner ! {ssl, Sock, Data},
+ transfer_messages(Pid, NewOwner);
+ {ssl_closed, Sock} ->
+ NewOwner ! {ssl_closed, Sock},
+ transfer_messages(Pid, NewOwner);
+ {ssl_error, Sock, Reason} ->
+ NewOwner ! {ssl_error, Sock, Reason},
+ transfer_messages(Pid, NewOwner)
+ after 0 ->
+ ok
+ end.
+
+%%
+%% debug(St, Format, Args) -> void() - printouts
+%%
+debug(St, Format, Args) ->
+ debug1(St#st.debug, St#st.brokertype, Format, Args).
+
+debug1(true, Type, Format0, Args) ->
+ {_MS, S, MiS} = erlang:now(),
+ Secs = S rem 100,
+ MiSecs = MiS div 1000,
+ Format = "++++ ~3..0w:~3..0w ssl_broker (~w)[~w]: " ++ Format0,
+ io:format(Format, [Secs, MiSecs, self(), Type| Args]);
+debug1(_, _, _, _) ->
+ ok.
+
+%%
+%% what(Reason) -> What
+%%
+what(Reason) when is_atom(Reason) ->
+ Reason;
+what({'EXIT', Reason}) ->
+ what(Reason);
+what({What, _Where}) when is_atom(What) ->
+ What;
+what(Reason) ->
+ Reason.
+
+
+%%
+%% OPTIONS
+%%
+%% Note that `accept' has no options when invoked, but get all its options
+%% by inheritance from `listen'.
+%%
+
+are_opt_tags(listener, OptTags) ->
+ is_subset(OptTags, listen_opt_tags());
+are_opt_tags(acceptor, OptTags) ->
+ is_subset(OptTags, accept_opt_tags());
+are_opt_tags(connector, OptTags) ->
+ is_subset(OptTags, connect_opt_tags()).
+
+listen_opt_tags() ->
+ tcp_listen_opt_tags() ++ ssl_opt_tags().
+
+accept_opt_tags() ->
+ tcp_gen_opt_tags().
+
+connect_opt_tags() ->
+ tcp_gen_opt_tags() ++ ssl_opt_tags().
+
+tcp_listen_opt_tags() ->
+ tcp_gen_opt_tags() ++ tcp_listen_only_opt_tags().
+
+tcp_gen_opt_tags() ->
+ %% All except `reuseaddr' and `deliver'.
+ [nodelay, active, packet, mode, header].
+
+tcp_listen_only_opt_tags() ->
+ [ip, backlog].
+
+ssl_opt_tags() ->
+ %% XXX Should remove cachetimeout.
+ [verify, depth, certfile, password, cacertfile, ciphers, cachetimeout].
+
+%% Options
+
+%%
+%% are_*_opts(Opts) -> boolean()
+%%
+are_connect_opts(Opts) ->
+ are_opts(fun is_connect_opt/1, Opts).
+
+are_listen_opts(Opts) ->
+ are_opts(fun is_listen_opt/1, Opts).
+
+are_opts(F, Opts) ->
+ lists:all(F, transform_opts(Opts)).
+
+%%
+%% get_*_opts(Opts) -> Value
+%%
+get_tcp_accept_opts(Opts) ->
+ [O || O <- transform_opts(Opts), is_tcp_accept_opt(O)].
+
+get_tcp_connect_opts(Opts) ->
+ [O || O <- transform_opts(Opts), is_tcp_connect_opt(O)].
+
+get_tcp_listen_opts(Opts) ->
+ [O || O <- transform_opts(Opts), is_tcp_listen_opt(O)].
+
+get_ssl_opts(Opts) ->
+ [O || O <- transform_opts(Opts), is_ssl_opt(O)].
+
+get_active(Opts) ->
+ get_tagged_opt(active, Opts, true).
+
+get_backlog(Opts) ->
+ get_tagged_opt(backlog, Opts, ?DEF_BACKLOG).
+
+get_ip(Opts) ->
+ get_tagged_opt(ip, Opts, {0, 0, 0, 0}).
+
+get_port(Opts) ->
+ get_tagged_opt(port, Opts, 0).
+
+get_nodelay(Opts) ->
+ get_tagged_opt(nodelay, Opts, empty).
+
+%%
+%% add_default_*_opts(Opts) -> NOpts
+%%
+
+add_default_tcp_accept_opts(Opts) ->
+ add_default_opts(Opts, default_tcp_accept_opts()).
+
+add_default_tcp_connect_opts(Opts) ->
+ add_default_opts(Opts, default_tcp_connect_opts()).
+
+add_default_tcp_listen_opts(Opts) ->
+ add_default_opts(Opts, default_tcp_listen_opts()).
+
+add_default_ssl_opts(Opts) ->
+ add_default_opts(Opts, default_ssl_opts()).
+
+add_default_opts(Opts, DefOpts) ->
+ TOpts = transform_opts(Opts),
+ TOpts ++ [DP || {DTag, _DVal} = DP <- DefOpts,
+ not lists:keymember(DTag, 1, TOpts)].
+
+default_tcp_accept_opts() ->
+ [O || O <- default_opts(), is_tcp_accept_opt(O)].
+
+default_tcp_connect_opts() ->
+ [O || O <- default_opts(), is_tcp_connect_opt(O)].
+
+default_tcp_listen_opts() ->
+ [O || O <- default_opts(), is_tcp_listen_opt(O)].
+
+default_ssl_opts() ->
+ [O || O <- default_opts(), is_ssl_opt(O)].
+
+default_opts() ->
+ [{mode, list}, {packet, 0}, {nodelay, false}, {active, true},
+ {backlog, ?DEF_BACKLOG}, {ip, {0, 0, 0, 0}},
+ {verify, 0}, {depth, 1}].
+
+
+%% Transform from old to new options, and also from old gen_tcp
+%% options to new ones. All returned options are tagged options.
+%%
+transform_opts(Opts) ->
+ lists:flatmap(fun transform_opt/1, Opts).
+
+transform_opt(binary) -> [{mode, binary}];
+transform_opt(list) -> [{mode, list}];
+transform_opt({packet, raw}) -> [{packet, 0}];
+transform_opt(raw) -> [];
+transform_opt(Opt) -> [Opt].
+
+%% NOTE: The is_*_opt/1 functions must be applied on transformed options
+%% only.
+
+is_connect_opt(Opt) ->
+ is_tcp_connect_opt(Opt) or is_ssl_opt(Opt).
+
+is_listen_opt(Opt) ->
+ is_tcp_listen_opt(Opt) or is_ssl_opt(Opt).
+
+is_tcp_accept_opt(Opt) ->
+ is_tcp_gen_opt(Opt).
+
+is_tcp_connect_opt(Opt) ->
+ is_tcp_gen_opt(Opt) or is_tcp_connect_only_opt(Opt).
+
+is_tcp_listen_opt(Opt) ->
+ is_tcp_gen_opt(Opt) or is_tcp_listen_only_opt(Opt).
+
+%% General options supported by gen_tcp: All except `reuseaddr' and
+%% `deliver'.
+is_tcp_gen_opt({mode, list}) -> true;
+is_tcp_gen_opt({mode, binary}) -> true;
+is_tcp_gen_opt({header, Sz}) when is_integer(Sz), 0 =< Sz -> true;
+is_tcp_gen_opt({packet, Sz}) when is_integer(Sz), 0 =< Sz, Sz =< 4-> true;
+is_tcp_gen_opt({packet, sunrm}) -> true;
+is_tcp_gen_opt({packet, asn1}) -> true;
+is_tcp_gen_opt({packet, cdr}) -> true;
+is_tcp_gen_opt({packet, fcgi}) -> true;
+is_tcp_gen_opt({packet, line}) -> true;
+is_tcp_gen_opt({packet, tpkt}) -> true;
+is_tcp_gen_opt({packet, http}) -> true;
+is_tcp_gen_opt({packet, httph}) -> true;
+is_tcp_gen_opt({nodelay, true}) -> true;
+is_tcp_gen_opt({nodelay, false}) -> true;
+is_tcp_gen_opt({active, true}) -> true;
+is_tcp_gen_opt({active, false}) -> true;
+is_tcp_gen_opt({active, once}) -> true;
+is_tcp_gen_opt({keepalive, true}) -> true;
+is_tcp_gen_opt({keepalive, false}) -> true;
+is_tcp_gen_opt({ip, Addr}) -> is_ip_address(Addr);
+is_tcp_gen_opt(_Opt) -> false.
+
+is_tcp_listen_only_opt({backlog, Size}) when is_integer(Size), 0 =< Size ->
+ true;
+is_tcp_listen_only_opt({reuseaddr, Bool}) when is_boolean(Bool) ->
+ true;
+is_tcp_listen_only_opt(_Opt) -> false.
+
+is_tcp_connect_only_opt({port, Port}) when is_integer(Port), 0 =< Port -> true;
+is_tcp_connect_only_opt(_Opt) -> false.
+
+%% SSL options
+
+is_ssl_opt({verify, Code}) when 0 =< Code, Code =< 2 -> true;
+is_ssl_opt({depth, Depth}) when 0 =< Depth -> true;
+is_ssl_opt({certfile, String}) -> is_string(String);
+is_ssl_opt({keyfile, String}) -> is_string(String);
+is_ssl_opt({password, String}) -> is_string(String);
+is_ssl_opt({cacertfile, String}) -> is_string(String);
+is_ssl_opt({ciphers, String}) -> is_string(String);
+is_ssl_opt({cachetimeout, Timeout}) when Timeout >= 0 -> true;
+is_ssl_opt(_Opt) -> false.
+
+%% Various types
+is_string(String) when is_list(String) ->
+ lists:all(fun (C) when is_integer(C), 0 =< C, C =< 255 -> true;
+ (_C) -> false end,
+ String);
+is_string(_) ->
+ false.
+
+is_ip_address(Addr) when tuple_size(Addr) =:= 4 ->
+ is_string(tuple_to_list(Addr));
+is_ip_address(Addr) when is_list(Addr) ->
+ is_string(Addr);
+is_ip_address(_) ->
+ false.
+
+get_tagged_opt(Tag, Opts, Default) ->
+ case lists:keysearch(Tag, 1, Opts) of
+ {value, {_, Value}} ->
+ Value;
+ _Other ->
+ Default
+ end.
+
+%%
+%% mk_ssl_optstr(Opts) -> string()
+%%
+%% Makes a "command line" string of SSL options
+%%
+mk_ssl_optstr(Opts) ->
+ lists:flatten([mk_one_ssl_optstr(O) || O <- Opts]).
+
+mk_one_ssl_optstr({verify, Code}) ->
+ [" -verify ", integer_to_list(Code)];
+mk_one_ssl_optstr({depth, Depth}) ->
+ [" -depth ", integer_to_list(Depth)];
+mk_one_ssl_optstr({certfile, String}) ->
+ [" -certfile ", String];
+mk_one_ssl_optstr({keyfile, String}) ->
+ [" -keyfile ", String];
+mk_one_ssl_optstr({password, String}) ->
+ [" -password ", String];
+mk_one_ssl_optstr({cacertfile, String}) ->
+ [" -cacertfile ", String];
+mk_one_ssl_optstr({ciphers, String}) ->
+ [" -ciphers ", String];
+mk_one_ssl_optstr({cachetimeout, Timeout}) ->
+ [" -cachetimeout ", integer_to_list(Timeout)];
+mk_one_ssl_optstr(_) ->
+ "".
+
+extract_opts(OptTags, Opts) ->
+ [O || O = {Tag,_} <- Opts, lists:member(Tag, OptTags)].
+
+replace_opts(NOpts, Opts) ->
+ lists:foldl(fun({Key, Val}, Acc) ->
+ lists:keyreplace(Key, 1, Acc, {Key, Val});
+ %% XXX Check. Patch from Chandrashekhar Mullaparthi.
+ (binary, Acc) ->
+ lists:keyreplace(mode, 1, Acc, {mode, binary})
+ end,
+ Opts, NOpts).
+
+%% Misc
+
+is_subset(A, B) ->
+ [] =:= A -- B.
diff --git a/lib/ssl/src/ssl_broker_int.hrl b/lib/ssl/src/ssl_broker_int.hrl
new file mode 100644
index 0000000000..b791485725
--- /dev/null
+++ b/lib/ssl/src/ssl_broker_int.hrl
@@ -0,0 +1,38 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-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: record definitions shared between ssl_prim.erl and ssl_broker.erl
+
+-record(st, {brokertype = nil, % connector | listener | acceptor
+ server = nil, % pid of ssl_server
+ client = nil, % client pid
+ collector = nil, % client pid, or collector during change of
+ % controlling process
+ fd = nil, % fd of "external" socket in port program
+ active = true, % true | false | once
+ opts = [], % options
+ thissock = nil, % this sslsocket
+ proxysock = nil, % local proxy socket within Erlang
+ proxyport = nil, % local port for proxy within Erlang
+ status = nil, % open | closing | closed
+ encrypted = false, %
+ debug = false %
+ }).
diff --git a/lib/ssl/src/ssl_broker_sup.erl b/lib/ssl/src/ssl_broker_sup.erl
new file mode 100644
index 0000000000..6d56a5fcf6
--- /dev/null
+++ b/lib/ssl/src/ssl_broker_sup.erl
@@ -0,0 +1,46 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-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 : Supervisor for brokers
+
+-module(ssl_broker_sup).
+
+-behaviour(supervisor).
+
+-export([start_link/0]).
+
+%% supervisor callbacks
+-export([init/1]).
+
+start_link() ->
+ supervisor:start_link({local, ssl_broker_sup}, ssl_broker_sup,
+ []).
+
+init([]) ->
+ {ok, {{simple_one_for_one, 10, 3600},
+ [{ssl_broker,
+ {ssl_broker, start_link, []},
+ temporary,
+ 100,
+ worker,
+ [ssl_broker]}
+ ]}}.
+
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
new file mode 100644
index 0000000000..d97b61a5ce
--- /dev/null
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -0,0 +1,156 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Help funtions for handling certificat verification.
+%% The path validation defined in ssl_handshake.erl that mainly
+%% calls functions in this module is described in RFC 3280.
+%%----------------------------------------------------------------------
+
+-module(ssl_certificate).
+
+-include("ssl_handshake.hrl").
+-include("ssl_alert.hrl").
+-include("ssl_internal.hrl").
+-include("ssl_debug.hrl").
+
+-export([trusted_cert_and_path/3,
+ certificate_chain/2,
+ file_to_certificats/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 =
+ case public_key:pkix_is_self_signed(OtpCert) of
+ true ->
+ {ok, IssuerId} = public_key:pkix_issuer_id(OtpCert, self),
+ {IssuerId, RestPath};
+ false ->
+ case public_key:pkix_issuer_id(OtpCert, other) of
+ {ok, IssuerId} ->
+ {IssuerId, [Cert | RestPath]};
+ {error, issuer_not_found} ->
+ case find_issuer(OtpCert, no_candidate) of
+ {ok, IssuerId} ->
+ {IssuerId, [Cert | RestPath]};
+ Other ->
+ {Other, RestPath}
+ 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
+ {ok, {BinCert,_}} ->
+ {BinCert, Path, []};
+ _ ->
+ %% Fail if verify = true
+ not_valid(?ALERT_REC(?FATAL, ?UNKNOWN_CA),
+ Verify, {Cert, RestPath})
+ end
+ end.
+
+
+certificate_chain(undefined, _CertsDbRef) ->
+ {error, no_cert};
+certificate_chain(OwnCert, CertsDbRef) ->
+ {ok, ErlCert} = public_key:pkix_decode_cert(OwnCert, otp),
+ certificate_chain(ErlCert, OwnCert, CertsDbRef, [OwnCert]).
+
+file_to_certificats(File) ->
+ {ok, List} = ssl_manager:cache_pem_file(File),
+ [Bin || {cert, Bin, not_encrypted} <- List].
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+certificate_chain(OtpCert, _Cert, CertsDbRef, Chain) ->
+ IssuerAndSelfSigned =
+ case public_key:pkix_is_self_signed(OtpCert) of
+ true ->
+ {public_key:pkix_issuer_id(OtpCert, self), true};
+ false ->
+ {public_key:pkix_issuer_id(OtpCert, other), false}
+ end,
+
+ case IssuerAndSelfSigned of
+ {_, true = SelfSigned} ->
+ certificate_chain(CertsDbRef, Chain, ignore, ignore, SelfSigned);
+ {{error, issuer_not_found}, SelfSigned} ->
+ case find_issuer(OtpCert, no_candidate) of
+ {ok, {SerialNr, Issuer}} ->
+ certificate_chain(CertsDbRef, Chain,
+ SerialNr, Issuer, SelfSigned);
+ _ ->
+ %% Guess the the issuer must be the root
+ %% certificate. The verification of the
+ %% cert chain will fail if guess is
+ %% incorrect.
+ {ok, lists:reverse(Chain)}
+ end;
+ {{ok, {SerialNr, Issuer}}, SelfSigned} ->
+ certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, SelfSigned)
+ end.
+
+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,
+ SerialNr, Issuer) of
+ {ok, {IssuerCert, ErlCert}} ->
+ {ok, ErlCert} = public_key:pkix_decode_cert(IssuerCert, otp),
+ certificate_chain(ErlCert, IssuerCert,
+ CertsDbRef, [IssuerCert | Chain]);
+ _ ->
+ %% The trusted cert may be obmitted from the chain as the
+ %% counter part needs to have it anyway to be able to
+ %% verify it. This will be the normal case for servers
+ %% that does not verify the clients and hence have not
+ %% specified the cacertfile.
+ {ok, lists:reverse(Chain)}
+ end.
+
+find_issuer(OtpCert, PrevCandidateKey) ->
+ case ssl_certificate_db:issuer_candidate(PrevCandidateKey) of
+ no_more_candidates ->
+ {error, issuer_not_found};
+ {Key, {_Cert, ErlCertCandidate}} ->
+ case public_key:pkix_is_issuer(OtpCert, ErlCertCandidate) of
+ true ->
+ public_key:pkix_issuer_id(ErlCertCandidate, self);
+ false ->
+ find_issuer(OtpCert, Key)
+ end
+ end.
+
+not_valid(Alert, true, _) ->
+ throw(Alert);
+not_valid(_, false, {ErlCert, Path}) ->
+ {ErlCert, Path, [{bad_cert, unknown_ca}]}.
diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl
new file mode 100644
index 0000000000..decc6c9fea
--- /dev/null
+++ b/lib/ssl/src/ssl_certificate_db.erl
@@ -0,0 +1,219 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Storage for trused certificats
+%%----------------------------------------------------------------------
+
+-module(ssl_certificate_db).
+
+-include_lib("public_key/include/public_key.hrl").
+
+-export([create/0, remove/1, add_trusted_certs/3,
+ remove_trusted_certs/2, lookup_trusted_cert/3, issuer_candidate/1,
+ cache_pem_file/3]).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: create() -> Db
+%% Db = term() - Reference to the crated database
+%%
+%% Description: Creates a new certificate db.
+%% Note: lookup_trusted_cert/3 may be called from any process but only
+%% the process that called create may call the other functions.
+%%--------------------------------------------------------------------
+create() ->
+ [ets:new(certificate_db_name(), [named_table, set, protected]),
+ ets:new(ssl_file_to_ref, [named_table, set, protected]),
+ ets:new(ssl_pid_to_file, [bag, private])].
+
+%%--------------------------------------------------------------------
+%% Function: delete(Db) -> _
+%% Db = Database refererence as returned by create/0
+%%
+%% Description: Removes database db
+%%--------------------------------------------------------------------
+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()
+%%
+%% Description: Retrives the trusted certificate identified by
+%% <SerialNumber, Issuer>. Ref is used as it is specified
+%% for each connection which certificates are trusted.
+%%--------------------------------------------------------------------
+lookup_trusted_cert(Ref, SerialNumber, Issuer) ->
+ case lookup({Ref, SerialNumber, Issuer}, certificate_db_name()) of
+ undefined ->
+ undefined;
+ [Certs] ->
+ {ok, Certs}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: add_trusted_certs(Pid, File, Db) -> {ok, Ref}
+%% Pid = pid()
+%% File = string()
+%% Db = Database refererence as returned by create/0
+%% Ref = 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, File, [CertsDb, FileToRefDb, PidToFileDb]) ->
+ Ref = case lookup(File, FileToRefDb) of
+ undefined ->
+ NewRef = make_ref(),
+ add_certs_from_file(File, NewRef, CertsDb),
+ insert(File, NewRef, 1, FileToRefDb),
+ NewRef;
+ [OldRef] ->
+ ref_count(File,FileToRefDb,1),
+ OldRef
+ end,
+ insert(Pid, File, PidToFileDb),
+ {ok, Ref}.
+
+%%--------------------------------------------------------------------
+%% Function: cache_pem_file(Pid, File, Db) -> FileContent
+%%
+%% Description: Cache file as binary in DB
+%%--------------------------------------------------------------------
+cache_pem_file(Pid, File, [_CertsDb, FileToRefDb, PidToFileDb]) ->
+ try ref_count(File, FileToRefDb,1)
+ catch _:_ ->
+ {ok, Content} = public_key:pem_to_der(File),
+ insert(File,Content,1,FileToRefDb)
+ end,
+ insert(Pid, File, PidToFileDb),
+ {ok, FileToRefDb}.
+
+%%--------------------------------------------------------------------
+%% Function: remove_trusted_certs(Pid, Db) -> _
+%%
+%% Description: Removes trusted certs originating from
+%% the file associated to Pid from the runtime database.
+%%--------------------------------------------------------------------
+remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) ->
+ Files = lookup(Pid, PidToFileDb),
+ delete(Pid, PidToFileDb),
+ Clear = fun(File) ->
+ case ref_count(File, FileToRefDb, -1) of
+ 0 ->
+ case lookup(File, FileToRefDb) of
+ [Ref] when is_reference(Ref) ->
+ remove_certs(Ref, CertsDb);
+ _ -> ok
+ end,
+ delete(File, FileToRefDb);
+ _ ->
+ ok
+ end
+ end,
+ case Files of
+ undefined -> ok;
+ _ ->
+ [Clear(File) || File <- Files],
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: issuer_candidate() -> {Key, Candidate} | 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.
+%%--------------------------------------------------------------------
+issuer_candidate(no_candidate) ->
+ Db = certificate_db_name(),
+ case ets:first(Db) of
+ '$end_of_table' ->
+ no_more_candidates;
+ Key ->
+ [Cert] = lookup(Key, Db),
+ {Key, Cert}
+ end;
+
+issuer_candidate(PrevCandidateKey) ->
+ Db = certificate_db_name(),
+ case ets:next(Db, PrevCandidateKey) of
+ '$end_of_table' ->
+ no_more_candidates;
+ Key ->
+ [Cert] = lookup(Key, Db),
+ {Key, Cert}
+ end.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+certificate_db_name() ->
+ ssl_otp_certificate_db.
+
+insert(Key, Data, Db) ->
+ true = ets:insert(Db, {Key, Data}).
+
+insert(Key, Data, Count, Db) ->
+ true = ets:insert(Db, {Key, Count, Data}).
+
+ref_count(Key, Db,N) ->
+ ets:update_counter(Db,Key,N).
+
+delete(Key, Db) ->
+ true = ets:delete(Db, Key).
+
+lookup(Key, Db) ->
+ case ets:lookup(Db, Key) of
+ [] ->
+ undefined;
+ Contents ->
+ Pick = fun({_, Data}) -> Data;
+ ({_,_,Data}) -> Data
+ end,
+ [Pick(Data) || Data <- Contents]
+ end.
+
+remove_certs(Ref, CertsDb) ->
+ ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}).
+
+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].
+
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
new file mode 100644
index 0000000000..3d3d11b7f3
--- /dev/null
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -0,0 +1,784 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Help functions for handling the SSL ciphers
+%%
+%%----------------------------------------------------------------------
+
+-module(ssl_cipher).
+
+-include("ssl_internal.hrl").
+-include("ssl_record.hrl").
+-include("ssl_cipher.hrl").
+-include("ssl_debug.hrl").
+
+-export([security_parameters/2, suite_definition/1,
+ decipher/4, cipher/4,
+ suite/1, suites/1,
+ openssl_suite/1, openssl_suite_name/1]).
+
+-compile(inline).
+
+%%--------------------------------------------------------------------
+%% Function: security_parameters(CipherSuite, SecParams) ->
+%% #security_parameters{}
+%%
+%% CipherSuite - as defined in ssl_cipher.hrl
+%% SecParams - #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),
+ SecParams#security_parameters{
+ cipher_suite = CipherSuite,
+ bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher),
+ cipher_type = type(Cipher),
+ key_size = effective_key_bits(Cipher),
+ expanded_key_material_length = expanded_key_material(Cipher),
+ key_material_length = key_material(Cipher),
+ iv_size = iv_size(Cipher),
+ mac_algorithm = mac_algorithm(Hash),
+ hash_size = hash_size(Hash),
+ exportable = Exportable}.
+
+%%--------------------------------------------------------------------
+%% Function: cipher(Method, CipherState, Mac, Data) ->
+%% {Encrypted, UpdateCipherState}
+%%
+%% 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
+%%-------------------------------------------------------------------
+cipher(?NULL, CipherState, <<>>, Fragment) ->
+ GenStreamCipherList = [Fragment, <<>>],
+ {GenStreamCipherList, CipherState};
+cipher(?RC4, CipherState, Mac, Fragment) ->
+ State0 = case CipherState#cipher_state.state of
+ undefined -> crypto:rc4_set_key(CipherState#cipher_state.key);
+ 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)
+ end, block_size(des_cbc), CipherState, Mac, Fragment);
+cipher(?AES, CipherState, Mac, Fragment) ->
+ block_cipher(fun(Key, IV, T) when byte_size(Key) =:= 16 ->
+ 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);
+%% 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()
+%%
+%% Description: Decrypts the data and the mac using method, updating
+%% the cipher state
+%%-------------------------------------------------------------------
+decipher(?NULL, _HashSz, CipherState, Fragment) ->
+ {Fragment, <<>>, CipherState};
+decipher(?RC4, HashSz, CipherState, Fragment) ->
+ ?DBG_TERM(CipherState#cipher_state.key),
+ 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) ->
+ block_decipher(fun(Key, IV, T) ->
+ crypto:des_cbc_decrypt(Key, IV, T)
+ end, CipherState, HashSz, Fragment);
+decipher(?'3DES', HashSz, CipherState, Fragment) ->
+ 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) ->
+ 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) ->
+%% 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).
+
+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}.
+
+%%--------------------------------------------------------------------
+%% Function: suites(Version) -> [Suite]
+%%
+%% Version = version()
+%% Suite = binary() from ssl_cipher.hrl
+%%
+%% Description: Returns a list of supported cipher suites.
+%%--------------------------------------------------------------------
+suites({3, 0}) ->
+ ssl_ssl3:suites();
+suites({3, N}) when N == 1; N == 2 ->
+ ssl_tls1:suites().
+
+%%--------------------------------------------------------------------
+%% Function: suite_definition(CipherSuite) ->
+%% {KeyExchange, Cipher, Hash, Exportable}
+%%
+%%
+%% 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 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
+%%-------------------------------------------------------------------
+%% 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};
+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};
+suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) ->
+ {dhe_dss, des_cbc, sha, no_export};
+suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
+ {dhe_dss, '3des_ede_cbc', sha, no_export};
+suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
+ {dhe_rsa, des_cbc, sha, no_export};
+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};
+
+%%% 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_DHE_DSS_WITH_AES_128_CBC_SHA) ->
+ {dhe_dss, aes_128_cbc, sha, ignore};
+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};
+suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
+ {dhe_dss, aes_256_cbc, sha, ignore};
+suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
+ {dhe_rsa, aes_256_cbc, sha, ignore};
+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}.
+
+%% 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}) ->
+ ?TLS_RSA_WITH_RC4_128_MD5;
+suite({rsa, rc4_128, sha, no_export}) ->
+ ?TLS_RSA_WITH_RC4_128_SHA;
+%% suite({rsa, idea_cbc, sha, no_export}) ->
+%% ?TLS_RSA_WITH_IDEA_CBC_SHA;
+suite({rsa, des_cbc, sha, no_export}) ->
+ ?TLS_RSA_WITH_DES_CBC_SHA;
+suite({rsa, '3des_ede_cbc', sha, no_export}) ->
+ ?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}) ->
+ ?TLS_DHE_DSS_WITH_DES_CBC_SHA;
+suite({dhe_dss, '3des_ede_cbc', sha, no_export}) ->
+ ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA;
+suite({dhe_rsa, des_cbc, sha, no_export}) ->
+ ?TLS_DHE_RSA_WITH_DES_CBC_SHA;
+suite({dhe_rsa, '3des_ede_cbc', sha, no_export}) ->
+ ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
+suite({dh_anon, rc4_128, md5, no_export}) ->
+ ?TLS_DH_anon_WITH_RC4_128_MD5;
+suite({dh_anon, des40_cbc, sha, no_export}) ->
+ ?TLS_DH_anon_WITH_DES_CBC_SHA;
+suite({dh_anon, '3des_ede_cbc', sha, no_export}) ->
+ ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+
+%%% TSL V1.1 AES suites
+suite({rsa, aes_128_cbc, sha, ignore}) ->
+ ?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}) ->
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA;
+suite({dhe_rsa, aes_128_cbc, sha, ignore}) ->
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
+suite({dh_anon, aes_128_cbc, sha, ignore}) ->
+ ?TLS_DH_anon_WITH_AES_128_CBC_SHA;
+suite({rsa, aes_256_cbc, sha, ignore}) ->
+ ?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}) ->
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA;
+suite({dhe_rsa, aes_256_cbc, sha, ignore}) ->
+ ?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.
+
+
+%% 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") ->
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA;
+openssl_suite("AES256-SHA") ->
+ ?TLS_RSA_WITH_AES_256_CBC_SHA;
+openssl_suite("EDH-RSA-DES-CBC3-SHA") ->
+ ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
+openssl_suite("EDH-DSS-DES-CBC3-SHA") ->
+ ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA;
+openssl_suite("DES-CBC3-SHA") ->
+ ?TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+openssl_suite("DHE-RSA-AES128-SHA") ->
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
+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.
+
+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) ->
+ "DHE-DSS-AES256-SHA";
+openssl_suite_name(?TLS_RSA_WITH_AES_256_CBC_SHA) ->
+ "AES256-SHA";
+openssl_suite_name(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
+ "EDH-RSA-DES-CBC3-SHA";
+openssl_suite_name(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
+ "EDH-DSS-DES-CBC3-SHA";
+openssl_suite_name(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) ->
+ "DES-CBC3-SHA";
+openssl_suite_name( ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) ->
+ "DHE-RSA-AES128-SHA";
+openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
+ "DHE-DSS-AES128-SHA";
+openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
+ "AES128-SHA";
+%% openssl_suite_name(?TLS_RSA_WITH_IDEA_CBC_SHA) ->
+%% "IDEA-CBC-SHA";
+openssl_suite_name(?TLS_RSA_WITH_RC4_128_SHA) ->
+ "RC4-SHA";
+openssl_suite_name(?TLS_RSA_WITH_RC4_128_MD5) ->
+ "RC4-MD5";
+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).
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+bulk_cipher_algorithm(null) ->
+ ?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 ->
+ ?RC4;
+bulk_cipher_algorithm(des40_cbc) ->
+ ?DES40;
+bulk_cipher_algorithm(des_cbc) ->
+ ?DES;
+bulk_cipher_algorithm('3des_ede_cbc') ->
+ ?'3DES';
+bulk_cipher_algorithm(Cipher) when Cipher == aes_128_cbc;
+ Cipher == aes_256_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;
+ Cipher == aes_256_cbc ->
+ ?BLOCK.
+
+key_material(null) ->
+ 0;
+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') ->
+ 24;
+key_material(aes_128_cbc) ->
+ 16;
+key_material(aes_256_cbc) ->
+ 32.
+
+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 ->
+ 8;
+expanded_key_material('3des_ede_cbc') ->
+ 24;
+expanded_key_material(Cipher) when Cipher == aes_128_cbc;
+ Cipher == aes_256_cbc ->
+ unknown.
+
+
+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 ->
+ 56;
+effective_key_bits(Cipher) when Cipher == idea_cbc;
+ Cipher == rc4_128;
+ Cipher == aes_128_cbc ->
+ 128;
+effective_key_bits('3des_ede_cbc') ->
+ 168;
+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;
+
+block_size(Cipher) when Cipher == aes_128_cbc;
+ Cipher == aes_256_cbc ->
+ 16.
+
+mac_algorithm(null) ->
+ ?NULL;
+mac_algorithm(md5) ->
+ ?MD5;
+mac_algorithm(sha) ->
+ ?SHA.
+
+hash_size(null) ->
+ 0;
+hash_size(md5) ->
+ 16;
+hash_size(sha) ->
+ 20.
+
+generic_block_cipher_from_bin(T, HashSize) ->
+ Sz1 = byte_size(T) - 1,
+ <<_:Sz1/binary, ?BYTE(PadLength)>> = T,
+ CompressedLength = byte_size(T) - PadLength - 1 - HashSize,
+ <<Content:CompressedLength/binary, Mac:HashSize/binary,
+ Padding:PadLength/binary, ?BYTE(PadLength)>> = T,
+ #generic_block_cipher{content=Content, mac=Mac,
+ padding=Padding, padding_length=PadLength}.
+
+generic_stream_cipher_from_bin(T, HashSz) ->
+ Sz = byte_size(T),
+ CompressedLength = Sz - HashSz,
+ <<Content:CompressedLength/binary, Mac:HashSz/binary>> = T,
+ #generic_stream_cipher{content=Content,
+ mac=Mac}.
+
+check_padding(_GBC) ->
+ ok.
+
+get_padding(Length, BlockSize) ->
+ get_padding_aux(BlockSize, Length rem BlockSize).
+
+get_padding_aux(_, 0) ->
+ {0, <<>>};
+get_padding_aux(BlockSize, PadLength) ->
+ N = BlockSize - PadLength,
+ {N, list_to_binary(lists:duplicate(N, N))}.
+
+next_iv(Bin, IV) ->
+ BinSz = byte_size(Bin),
+ IVSz = byte_size(IV),
+ FirstPart = BinSz - IVSz,
+ <<_:FirstPart/binary, NextIV:IVSz/binary>> = Bin,
+ NextIV.
+
diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl
new file mode 100644
index 0000000000..4304c501b7
--- /dev/null
+++ b/lib/ssl/src/ssl_cipher.hrl
@@ -0,0 +1,253 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Record and constant defenitions for the SSL ciphers and
+%% the SSL-cipher protocol see RFC 4346, RFC 3268
+%%----------------------------------------------------------------------
+
+-ifndef(ssl_cipher).
+-define(ssl_cipher, true).
+
+%%% SSL cipher protocol %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-define(CHANGE_CIPHER_SPEC_PROTO, 1). % _PROTO to not clash with
+ % SSL record protocol
+
+-record(change_cipher_spec, {
+ type = 1
+ }).
+
+%%% SSL cipher suites %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% -record(cipher_state,
+%% {
+%% suite,
+%% name,
+%% state
+%% }).
+
+-record(cipher_state, {
+ iv,
+ key,
+ state
+ }).
+
+%%% TLS_NULL_WITH_NULL_NULL is specified and is the initial state of a
+%%% TLS connection during the first handshake on that channel, but
+%%% must not be negotiated, as it provides no more protection than an
+%%% unsecured connection.
+
+%% 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
+%%% 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.
+
+%% TLS_RSA_WITH_NULL_MD5 = { 0x00,0x01 };
+-define(TLS_RSA_WITH_NULL_MD5, <<?BYTE(16#00), ?BYTE(16#01)>>).
+
+%% 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)>>).
+
+%% TLS_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x0A };
+-define(TLS_RSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#0A)>>).
+
+%%% The following CipherSuite definitions are used for server-
+%%% authenticated (and optionally client-authenticated)
+%%% Diffie-Hellman. DH denotes cipher suites in which the server's
+%%% certificate contains the Diffie-Hellman parameters signed by the
+%%% certificate authority (CA). DHE denotes ephemeral Diffie-Hellman,
+%%% where the Diffie-Hellman parameters are signed by a DSS or RSA
+%%% certificate, which has been signed by the CA. The signing
+%%% algorithm used is specified after the DH or DHE parameter. The
+%%% server can request an RSA or DSS signature- capable certificate
+%%% from the client for client authentication or it may request a
+%%% Diffie-Hellman certificate. Any Diffie-Hellman certificate
+%%% 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)>>).
+
+%% TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = { 0x00,0x1B };
+-define(TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#1B)>>).
+
+
+%%% AES Cipher Suites RFC 3268
+
+%% TLS_RSA_WITH_AES_128_CBC_SHA = { 0x00, 0x2F };
+-define(TLS_RSA_WITH_AES_128_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#2F)>>).
+
+%% TLS_DH_DSS_WITH_AES_128_CBC_SHA = { 0x00, 0x30 };
+-define(TLS_DH_DSS_WITH_AES_128_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#30)>>).
+
+%% TLS_DH_RSA_WITH_AES_128_CBC_SHA = { 0x00, 0x31 };
+-define(TLS_DH_RSA_WITH_AES_128_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#31)>>).
+
+%% TLS_DHE_DSS_WITH_AES_128_CBC_SHA = { 0x00, 0x32 };
+-define(TLS_DHE_DSS_WITH_AES_128_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#32)>>).
+
+%% TLS_DHE_RSA_WITH_AES_128_CBC_SHA = { 0x00, 0x33 };
+-define(TLS_DHE_RSA_WITH_AES_128_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#33)>>).
+
+%% TLS_DH_anon_WITH_AES_128_CBC_SHA = { 0x00, 0x34 };
+-define(TLS_DH_anon_WITH_AES_128_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#34)>>).
+
+%% TLS_RSA_WITH_AES_256_CBC_SHA = { 0x00, 0x35 };
+-define(TLS_RSA_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#35)>>).
+
+%% TLS_DH_DSS_WITH_AES_256_CBC_SHA = { 0x00, 0x36 };
+-define(TLS_DH_DSS_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#36)>>).
+
+%% TLS_DH_RSA_WITH_AES_256_CBC_SHA = { 0x00, 0x37 };
+-define(TLS_DH_RSA_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#37)>>).
+
+%% TLS_DHE_DSS_WITH_AES_256_CBC_SHA = { 0x00, 0x38 };
+-define(TLS_DHE_DSS_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#38)>>).
+
+%% TLS_DHE_RSA_WITH_AES_256_CBC_SHA = { 0x00, 0x39 };
+-define(TLS_DHE_RSA_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#39)>>).
+
+%% TLS_DH_anon_WITH_AES_256_CBC_SHA = { 0x00, 0x3A };
+-define(TLS_DH_anon_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#3A)>>).
+
+%%% Kerberos Cipher Suites
+
+%% TLS_KRB5_WITH_DES_CBC_SHA = { 0x00,0x1E };
+-define(TLS_KRB5_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#1E)>>).
+
+%% TLS_KRB5_WITH_3DES_EDE_CBC_SHA = { 0x00,0x1F };
+-define(TLS_KRB5_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#1F)>>).
+
+%% TLS_KRB5_WITH_RC4_128_SHA = { 0x00,0x20 };
+-define(TLS_KRB5_WITH_RC4_128_SHA, <<?BYTE(16#00), ?BYTE(16#20)>>).
+
+%% TLS_KRB5_WITH_IDEA_CBC_SHA = { 0x00,0x21 };
+-define(TLS_KRB5_WITH_IDEA_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#21)>>).
+
+%% TLS_KRB5_WITH_DES_CBC_MD5 = { 0x00,0x22 };
+-define(TLS_KRB5_WITH_DES_CBC_MD5, <<?BYTE(16#00), ?BYTE(16#22)>>).
+
+%% TLS_KRB5_WITH_3DES_EDE_CBC_MD5 = { 0x00,0x23 };
+-define(TLS_KRB5_WITH_3DES_EDE_CBC_MD5, <<?BYTE(16#00), ?BYTE(16#23)>>).
+
+%% TLS_KRB5_WITH_RC4_128_MD5 = { 0x00,0x24 };
+-define(TLS_KRB5_WITH_RC4_128_MD5, <<?BYTE(16#00), ?BYTE(16#24)>>).
+
+%% 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)>>).
+
+-endif. % -ifdef(ssl_cipher).
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
new file mode 100644
index 0000000000..178c055cdf
--- /dev/null
+++ b/lib/ssl/src/ssl_connection.erl
@@ -0,0 +1,1704 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Handles an ssl connection, e.i. both the setup
+%% e.i. SSL-Handshake, SSL-Alert and SSL-Cipher protocols and delivering
+%% data to the application. All data on the connectinon is received and
+%% sent according to the SSL-record protocol.
+%%----------------------------------------------------------------------
+
+-module(ssl_connection).
+
+-behaviour(gen_fsm).
+
+-include("ssl_debug.hrl").
+-include("ssl_handshake.hrl").
+-include("ssl_alert.hrl").
+-include("ssl_record.hrl").
+-include("ssl_cipher.hrl").
+-include("ssl_internal.hrl").
+-include("ssl_int.hrl").
+-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,
+ new_user/2, get_opts/2, set_opts/2, info/1, session_info/1,
+ peer_certificate/1,
+ sockname/1, peername/1]).
+
+%% Called by ssl_connection_sup
+-export([start_link/7]).
+
+%% gen_fsm callbacks
+-export([init/1, hello/2, certify/2, cipher/2, connection/2, connection/3, abbreviated/2,
+ handle_event/3,
+ handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
+
+-record(state, {
+ role, % client | server
+ user_application, % {MonitorRef, pid()}
+ transport_cb, % atom() - callback module
+ data_tag, % atom() - ex tcp.
+ close_tag, % atom() - ex tcp_closed
+ host, % string() | ipadress()
+ port, % integer()
+ socket, % socket()
+ ssl_options, % #ssl_options{}
+ socket_options, % #socket_options{}
+ connection_states, % #connection_states{} from ssl_record.hrl
+ 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_cache, %
+ session_cache_cb, %
+ negotiated_version, % #protocol_version{}
+ supported_protocol_versions, % [atom()]
+ client_certificate_requested = false,
+ key_algorithm, % atom as defined by cipher_suite
+ public_key_info, % PKIX: {Algorithm, PublicKey, PublicKeyParams}
+ private_key, % PKIX: 'RSAPrivateKey'
+ diffie_hellman_params, %
+ premaster_secret, %
+ cert_db_ref, % ets_table()
+ 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 % boolan()
+ }).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+send(Pid, Data) ->
+ sync_send_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, infinity).
+send(Pid, Data, Timeout) ->
+ sync_send_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, Timeout).
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+recv(Pid, Length, Timeout) -> % TODO: Prio with renegotiate?
+ sync_send_all_state_event(Pid, {recv, Length}, Timeout).
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+connect(Host, Port, Socket, Options, User, CbInfo, Timeout) ->
+ start_fsm(client, Host, Port, Socket, Options, User, CbInfo,
+ Timeout).
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+accept(Port, Socket, Opts, User, CbInfo, Timeout) ->
+ start_fsm(server, "localhost", Port, Socket, Opts, User,
+ CbInfo, Timeout).
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+close(ConnectionPid) ->
+ case sync_send_all_state_event(ConnectionPid, close) of
+ {error, closed} ->
+ ok;
+ Other ->
+ Other
+ end.
+
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+shutdown(ConnectionPid, How) ->
+ sync_send_all_state_event(ConnectionPid, {shutdown, How}).
+
+
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+new_user(ConnectionPid, User) ->
+ sync_send_all_state_event(ConnectionPid, {new_user, User}).
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+sockname(ConnectionPid) ->
+ sync_send_all_state_event(ConnectionPid, sockname).
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+peername(ConnectionPid) ->
+ sync_send_all_state_event(ConnectionPid, peername).
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+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:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+set_opts(ConnectionPid, Options) ->
+ sync_send_all_state_event(ConnectionPid, {set_opts, Options}).
+
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+info(ConnectionPid) ->
+ sync_send_all_state_event(ConnectionPid, info).
+
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+session_info(ConnectionPid) ->
+ sync_send_all_state_event(ConnectionPid, session_info).
+
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+peer_certificate(ConnectionPid) ->
+ sync_send_all_state_event(ConnectionPid, peer_certificate).
+
+%%====================================================================
+%% ssl_connection_sup API
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%%
+%% Description: Creates a gen_fsm process which calls Module:init/1 to
+%% initialize. To ensure a synchronized start-up procedure, this function
+%% does not return until Module:init/1 has returned.
+%%--------------------------------------------------------------------
+start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
+ gen_fsm:start_link(?MODULE, [Role, Host, Port, Socket, Options,
+ User, CbInfo], []).
+
+
+%%====================================================================
+%% gen_fsm callbacks
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, StateName, State} |
+%% {ok, StateName, State, Timeout} |
+%% ignore |
+%% {stop, StopReason}
+%% 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,
+ User, CbInfo]) ->
+ State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
+ Hashes0 = ssl_handshake:init_hashes(),
+
+ try ssl_init(SSLOpts, Role) of
+ {ok, Ref, CacheRef, OwnCert, Key} ->
+ State = State0#state{tls_handshake_hashes = Hashes0,
+ own_cert = OwnCert,
+ cert_db_ref = Ref,
+ session_cache = CacheRef,
+ private_key = Key},
+ {ok, hello, State}
+ catch
+ throw:Error ->
+ {stop, Error}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function:
+%% state_name(Event, State) -> {next_state, NextStateName, NextState}|
+%% {next_state, NextStateName,
+%% NextState, Timeout} |
+%% {stop, Reason, NewState}
+%% 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}
+ = State0) ->
+ Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates, SslOpts),
+ 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,
+ 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)};
+
+hello(socket_control, #state{role = server} = State) ->
+ {next_state, hello, next_record(State)};
+
+hello(hello, #state{role = client} = State) ->
+ {next_state, hello, State};
+
+hello(#server_hello{cipher_suite = CipherSuite,
+ compression_method = Compression} = Hello,
+ #state{session = Session0 = #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),
+
+ 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
+ end;
+
+hello(Hello = #client_hello{client_version = ClientVersion},
+ State = #state{connection_states = ConnectionStates0,
+ port = Port, session = Session0,
+ session_cache = Cache,
+ session_cache_cb = CacheCb,
+ ssl_options = SslOpts}) ->
+
+ case ssl_handshake:hello(Hello, {Port, SslOpts,
+ Session0, Cache, CacheCb,
+ ConnectionStates0}) of
+ {Version, {Type, Session}, ConnectionStates} ->
+ do_server_hello(Type, State#state{connection_states =
+ ConnectionStates,
+ negotiated_version = Version,
+ session = Session});
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ClientVersion, hello, State),
+ {stop, normal, State}
+ end.
+
+abbreviated(socket_control, #state{role = server} = State) ->
+ {next_state, abbreviated, State};
+abbreviated(hello, State) ->
+ {next_state, certify, State};
+
+abbreviated(Finished = #finished{},
+ #state{role = server,
+ negotiated_version = Version,
+ tls_handshake_hashes = Hashes,
+ session = #session{master_secret = MasterSecret},
+ from = From} = State) ->
+ case ssl_handshake:verify_connection(Version, Finished, client,
+ MasterSecret, Hashes) of
+ verified ->
+ gen_fsm:reply(From, connected),
+ {next_state, connection, next_record_if_active(State)};
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, abbreviated, State),
+ {stop, normal, State}
+ end;
+
+abbreviated(Finished = #finished{},
+ #state{role = client, tls_handshake_hashes = Hashes0,
+ session = #session{master_secret = MasterSecret},
+ from = From,
+ negotiated_version = Version} = State) ->
+ case ssl_handshake:verify_connection(Version, Finished, server,
+ MasterSecret, Hashes0) of
+ verified ->
+ {ConnectionStates, Hashes} = finalize_client_handshake(State),
+ gen_fsm:reply(From, connected),
+ {next_state, connection,
+ next_record_if_active(State#state{tls_handshake_hashes = Hashes,
+ connection_states =
+ ConnectionStates})};
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, abbreviated, State),
+ {stop, normal, State}
+ end.
+
+certify(socket_control, #state{role = server} = State) ->
+ {next_state, certify, State};
+certify(hello, State) ->
+ {next_state, certify, State};
+
+certify(#certificate{asn1_certificates = []},
+ #state{role = server, negotiated_version = Version,
+ ssl_options = #ssl_options{verify = verify_peer,
+ fail_if_no_peer_cert = true}} =
+ State) ->
+ Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE),
+ handle_own_alert(Alert, Version, certify_certificate, State),
+ {stop, normal, State};
+
+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})};
+
+certify(#certificate{} = Cert,
+ #state{session = Session,
+ negotiated_version = Version,
+ cert_db_ref = CertDbRef,
+ ssl_options = Opts} = State0) ->
+ case ssl_handshake:certify(Cert, CertDbRef, Opts#ssl_options.depth,
+ Opts#ssl_options.verify,
+ Opts#ssl_options.verify_fun) of
+ {PeerCert, PublicKeyInfo} ->
+ State = State0#state{session =
+ Session#session{peer_certificate = PeerCert},
+ public_key_info = PublicKeyInfo,
+ client_certificate_requested = false
+ },
+ {next_state, certify, next_record(State)};
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, certify_certificate, State0),
+ {stop, normal, State0}
+ end;
+
+certify(#server_key_exchange{} = KeyExchangeMsg,
+ #state{role = client,
+ key_algorithm = Alg} = State)
+ when Alg == dhe_dss; Alg == dhe_rsa; Alg == dh_anon; Alg == krb5 ->
+ NewState = handle_server_key(KeyExchangeMsg, State),
+ {next_state, certify, NewState};
+
+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(KeyExchangeMsg = #server_key_exchange{}, State =
+ #state{role = server}) ->
+ NewState = handle_clinet_key(KeyExchangeMsg, State),
+ {next_state, cipher, NewState};
+
+certify(#certificate_request{}, State) ->
+ NewState = State#state{client_certificate_requested = true},
+ {next_state, certify, next_record(NewState)};
+
+certify(#server_hello_done{},
+ #state{session = Session0,
+ connection_states = ConnectionStates0,
+ negotiated_version = Version,
+ premaster_secret = PremasterSecret,
+ role = client} = State0) ->
+ case ssl_handshake:master_secret(Version, PremasterSecret,
+ ConnectionStates0, client) of
+ {MasterSecret, ConnectionStates1} ->
+ Session = Session0#session{master_secret = MasterSecret},
+ State = State0#state{connection_states = ConnectionStates1,
+ session = Session},
+ client_certify_and_key_exchange(State);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version,
+ certify_server_hello_done, State0),
+ {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}) ->
+ %% We expect a certificate here
+ Alert = ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE),
+ handle_own_alert(Alert, Version, certify_server_waiting_certificate, State),
+ {stop, normal, 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
+ catch
+ #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, State) ->
+ {next_state, cipher, 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) ->
+ case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
+ Version, MasterSecret,
+ Algorithm, Hashes) of
+ valid ->
+ {next_state, cipher, next_record(State)};
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, cipher, State),
+ {stop, normal, State}
+ end;
+
+cipher(#finished{} = Finished,
+ State = #state{from = From,
+ negotiated_version = Version,
+ host = Host,
+ port = Port,
+ role = Role,
+ session = #session{master_secret = MasterSecret}
+ = Session0,
+ tls_handshake_hashes = Hashes}) ->
+
+ case ssl_handshake:verify_connection(Version, Finished,
+ opposite_role(Role),
+ MasterSecret, Hashes) of
+ verified ->
+ gen_fsm:reply(From, connected),
+ Session = register_session(Role, Host, Port, Session0),
+ case Role of
+ client ->
+ {next_state, connection,
+ next_record_if_active(State#state{session = Session})};
+ server ->
+ {NewConnectionStates, NewHashes} =
+ finalize_server_handshake(State#state{
+ session = Session}),
+ NewState =
+ State#state{connection_states = NewConnectionStates,
+ session = Session,
+ tls_handshake_hashes = NewHashes},
+ {next_state, connection, next_record_if_active(NewState)}
+ end;
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, cipher, State),
+ {stop, normal, State}
+ end.
+
+connection(socket_control, #state{role = server} = State) ->
+ {next_state, connection, State};
+connection(hello, State = #state{host = Host, port = Port,
+ socket = Socket,
+ ssl_options = SslOpts,
+ negotiated_version = Version,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0,
+ tls_handshake_hashes = Hashes0}) ->
+
+ Hello = ssl_handshake:client_hello(Host, Port,
+ ConnectionStates0, SslOpts),
+ {BinMsg, ConnectionStates1, Hashes1} =
+ encode_handshake(Hello, Version, ConnectionStates0, Hashes0),
+ Transport:send(Socket, BinMsg),
+ {next_state, hello, State#state{connection_states = ConnectionStates1,
+ tls_handshake_hashes = Hashes1}}.
+
+%%--------------------------------------------------------------------
+%% Function:
+%% state_name(Event, From, 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}
+%% 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:sync_send_event/2,3, the instance of this function with the same
+%% name as the current state name StateName is called to handle the event.
+%%--------------------------------------------------------------------
+connection({application_data, Data}, _From,
+ State = #state{socket = Socket,
+ negotiated_version = Version,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0}) ->
+ %% We should look into having a worker process to do this to
+ %% parallize send and receive decoding and not block the receiver
+ %% if sending is overloading the socket.
+ {Msgs, ConnectionStates1} = encode_data(Data, Version, ConnectionStates0),
+ Result = Transport:send(Socket, Msgs),
+ {reply, Result,
+ connection, State#state{connection_states = ConnectionStates1}}.
+
+%%--------------------------------------------------------------------
+%% Function:
+%% handle_event(Event, StateName, State) -> {next_state, NextStateName,
+%% NextState} |
+%% {next_state, NextStateName,
+%% NextState, Timeout} |
+%% {stop, Reason, NewState}
+%% 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.
+%%--------------------------------------------------------------------
+handle_event(#ssl_tls{type = ?HANDSHAKE, fragment = Data},
+ StateName,
+ State = #state{key_algorithm = KeyAlg,
+ tls_handshake_buffer = Buf0,
+ negotiated_version = Version}) ->
+ Handle =
+ fun({Packet, Raw}, {next_state, SName, AS=#state{tls_handshake_hashes=Hs0}}) ->
+ Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
+ ?MODULE:SName(Packet, AS#state{tls_handshake_hashes=Hs1});
+ (_, StopState) -> StopState
+ end,
+ try
+ {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0, KeyAlg,Version),
+ Start = {next_state, StateName, State#state{tls_handshake_buffer = Buf}},
+ lists:foldl(Handle, Start, Packets)
+ catch throw:#alert{} = Alert ->
+ handle_own_alert(Alert, Version, StateName, State),
+ {stop, normal, State}
+ 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} = Alert, StateName,
+ #state{log_alert = Log} = State) ->
+ log_alert(Log, StateName, Alert),
+%%TODO: Could be user_canceled or no_negotiation should the latter be
+ %% treated as fatal?!
+ {next_state, StateName, next_record(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}
+%% 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.
+%%--------------------------------------------------------------------
+handle_sync_event(started, 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({shutdown, How}, From, StateName,
+ #state{transport_cb = CbModule,
+ socket = Socket} = State) ->
+ case CbModule:shutdown(Socket, How) of
+ ok ->
+ {reply, ok, StateName, State};
+ Error ->
+ {stop, normal, Error, State#state{from = From}}
+ end;
+
+%% TODO: men vad g�r next_record om det �r t.ex. renegotiate? kanske
+%% inte bra... t�l att t�nkas p�!
+handle_sync_event({recv, N}, From, StateName,
+ State0 = #state{user_data_buffer = Buffer}) ->
+ State1 = State0#state{bytes_to_read = N, from = From},
+ case Buffer of
+ <<>> ->
+ State = next_record(State1),
+ {next_state, StateName, State};
+ _ ->
+ case application_data(<<>>, State1) of
+ Stop = {stop, _, _} ->
+ Stop;
+ State ->
+ {next_state, StateName, State}
+ end
+ end;
+
+handle_sync_event({new_user, User}, _From, StateName,
+ State =#state{user_application = {OldMon, _}}) ->
+ NewMon = erlang:monitor(process, User),
+ erlang:demonitor(OldMon, [flush]),
+ {reply, ok, StateName, State#state{user_application = {NewMon,User}}};
+
+handle_sync_event({get_opts, OptTags}, _From, StateName,
+ #state{socket = Socket,
+ socket_options = SockOpts} = State) ->
+ OptsReply = get_socket_opts(Socket, OptTags, SockOpts, []),
+ {reply, OptsReply, StateName, State};
+
+handle_sync_event(sockname, _From, StateName,
+ #state{socket = Socket} = State) ->
+ SockNameReply = inet:sockname(Socket),
+ {reply, SockNameReply, StateName, State};
+
+handle_sync_event(peername, _From, StateName,
+ #state{socket = Socket} = State) ->
+ PeerNameReply = inet:peername(Socket),
+ {reply, PeerNameReply, StateName, State};
+
+handle_sync_event({set_opts, Opts0}, _From, StateName,
+ #state{socket_options = Opts1,
+ socket = Socket,
+ user_data_buffer = Buffer} = State0) ->
+ Opts = set_socket_opts(Socket, Opts0, Opts1, []),
+ State1 = State0#state{socket_options = Opts},
+ if
+ Opts#socket_options.active =:= false ->
+ {reply, ok, StateName, State1};
+ Buffer =:= <<>>, Opts1#socket_options.active =:= false ->
+ %% Need data, set active once
+ {reply, ok, StateName, next_record_if_active(State1)};
+ Buffer =:= <<>> ->
+ %% Active once already set
+ {reply, ok, StateName, State1};
+ true ->
+ case application_data(<<>>, State1) of
+ Stop = {stop,_,_} ->
+ Stop;
+ State ->
+ {reply, ok, StateName, State}
+ end
+ end;
+
+handle_sync_event(info, _, StateName,
+ #state{negotiated_version = Version,
+ session = #session{cipher_suite = Suite}} = State) ->
+
+ AtomVersion = ssl_record:protocol_version(Version),
+ {reply, {ok, {AtomVersion, ssl_cipher:suite_definition(Suite)}},
+ StateName, State};
+
+handle_sync_event(session_info, _, StateName,
+ #state{session = #session{session_id = Id,
+ cipher_suite = Suite}} = State) ->
+ {reply, [{session_id, Id},
+ {cipher_suite, ssl_cipher:suite_definition(Suite)}],
+ StateName, State};
+
+handle_sync_event(peer_certificate, _, StateName,
+ #state{session = #session{peer_certificate = Cert}}
+ = State) ->
+ {reply, {ok, Cert}, StateName, State}.
+
+
+%%--------------------------------------------------------------------
+%% Function:
+%% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}|
+%% {next_state, NextStateName, NextState,
+%% Timeout} |
+%% {stop, Reason, NewState}
+%% 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 =
+ #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})};
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, StateName, State),
+ {stop, normal, State}
+ end;
+
+%% %% This is the code for {packet,ssl} removed because it was slower
+%% %% than handling it in erlang.
+%% handle_info(Data = #ssl_tls{}, StateName,
+%% State = #state{tls_buffer = Buffer,
+%% socket = Socket,
+%% connection_states = ConnectionStates0}) ->
+%% case Buffer of
+%% buffer ->
+%% {next_state, StateName, State#state{tls_buffer = [Data]}};
+%% continue ->
+%% inet:setopts(Socket, [{active,once}]),
+%% {Plain, ConnectionStates} =
+%% ssl_record:decode_cipher_text(Data, ConnectionStates0),
+%% gen_fsm:send_all_state_event(self(), Plain),
+%% {next_state, StateName,
+%% State#state{tls_buffer = buffer,
+%% connection_states = ConnectionStates}};
+%% List when is_list(List) ->
+%% {next_state, StateName,
+%% State#state{tls_buffer = Buffer ++ [Data]}}
+%% end;
+
+%% handle_info(CloseMsg = {_, Socket}, StateName0,
+%% #state{socket = Socket,tls_buffer = [Msg]} = State0) ->
+%% %% Hmm we have a ssl_tls msg buffered, handle that first
+%% %% and it proberbly is a close alert
+%% {next_state, StateName0, State0#state{tls_buffer=[Msg,{ssl_close,CloseMsg}]}};
+
+handle_info({CloseTag, Socket}, _StateName,
+ #state{socket = Socket, close_tag = CloseTag,
+ negotiated_version = Version, host = Host,
+ port = Port, socket_options = Opts,
+ user_application = {_Mon,Pid}, from = From,
+ role = Role, session = Session} = State) ->
+ %% Debug option maybe, the user do NOT want to see these in their logs
+ %% error_logger:info_report("SSL: Peer did not send close notify alert."),
+ case Version of
+ {1, N} when N >= 1 ->
+ ok;
+ _ ->
+ invalidate_session(Role, Host, Port, Session)
+ end,
+ alert_user(Opts#socket_options.active, Pid, From,
+ ?ALERT_REC(?WARNING, ?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}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, StateName, State) -> void()
+%% 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, _S=#state{negotiated_version = Version,
+ connection_states = ConnectionStates,
+ transport_cb = Transport,
+ socket = Socket}) ->
+ {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING,?CLOSE_NOTIFY),
+ Version, ConnectionStates),
+ Transport:send(Socket, BinAlert),
+ Transport:close(Socket);
+terminate(_Reason, _StateName, _S=#state{transport_cb = Transport, socket = Socket}) ->
+ Transport:close(Socket),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function:
+%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, StateName, State, _Extra) ->
+ {ok, StateName, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+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}
+ 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),
+ ?DBG_TERM(PrivateKey),
+ {ok, CertDbRef, CacheRef, OwnCert, PrivateKey}.
+
+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, _Error} ->
+ Report = io_lib:format("SSL: Error ~p ~n",[_Error]),
+ error_logger:error_report(Report),
+ throw(ecacertfile)
+ end.
+
+init_certificates(CertDbRef, CacheRef, CertFile, client) ->
+ try
+ [OwnCert] = ssl_certificate:file_to_certificats(CertFile),
+ {ok, CertDbRef, CacheRef, OwnCert}
+ catch _E:_R ->
+ {ok, CertDbRef, CacheRef, undefined}
+ end;
+
+init_certificates(CertDbRef, CacheRef, CertFile, server) ->
+ try
+ [OwnCert] = ssl_certificate:file_to_certificats(CertFile),
+ {ok, CertDbRef, CacheRef, OwnCert}
+ catch _E:_R ->
+ Report = io_lib:format("SSL: ~p: ~p:~p ~p~n",
+ [?LINE, _E,_R, 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:_R ->
+ Report = io_lib:format("SSL: ~p: ~p:~p ~p~n",
+ [?LINE, _E,_R, erlang:get_stacktrace()]),
+ error_logger:error_report(Report),
+ throw(ekeyfile)
+ end;
+init_private_key(PrivateKey, _, _,_) ->
+ PrivateKey.
+
+send_event(FsmPid, Event) ->
+ gen_fsm:send_event(FsmPid, Event).
+
+sync_send_event(FsmPid, Event, Timeout) ->
+ try gen_fsm:sync_send_event(FsmPid, Event, Timeout) of
+ Reply ->
+ Reply
+ catch
+ exit:{noproc, _} ->
+ {error, closed};
+ exit:{timeout, _} ->
+ {error, timeout};
+ exit:{normal, _} ->
+ {error, closed}
+ end.
+
+
+
+send_all_state_event(FsmPid, Event) ->
+ gen_fsm:send_all_state_event(FsmPid, Event).
+
+sync_send_all_state_event(FsmPid, Event) ->
+ sync_send_all_state_event(FsmPid, Event, ?DEFAULT_TIMEOUT
+).
+
+sync_send_all_state_event(FsmPid, Event, Timeout) ->
+ try gen_fsm:sync_send_all_state_event(FsmPid, Event, Timeout)
+ catch
+ exit:{noproc, _} ->
+ {error, closed};
+ exit:{timeout, _} ->
+ {error, timeout};
+ exit:{normal, _} ->
+ {error, closed}
+ end.
+
+%% Events: #alert{}
+alert_event(Alert) ->
+ send_all_state_event(self(), Alert).
+
+certify_client(#state{client_certificate_requested = true, role = client,
+ connection_states = ConnectionStates0,
+ transport_cb = Transport,
+ negotiated_version = Version,
+ cert_db_ref = CertDbRef,
+ own_cert = OwnCert,
+ socket = Socket,
+ tls_handshake_hashes = Hashes0} = State) ->
+ Certificate = ssl_handshake:certificate(OwnCert, CertDbRef, client),
+ {BinCert, ConnectionStates1, Hashes1} =
+ encode_handshake(Certificate, Version, ConnectionStates0, Hashes0),
+ Transport:send(Socket, BinCert),
+ State#state{connection_states = ConnectionStates1,
+ tls_handshake_hashes = Hashes1};
+certify_client(#state{client_certificate_requested = false} = State) ->
+ State.
+
+verify_client_cert(#state{client_certificate_requested = true, role = client,
+ connection_states = ConnectionStates0,
+ transport_cb = Transport,
+ 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 ->
+ SigAlg = ssl_handshake:sig_alg(KeyAlg),
+ {BinVerified, ConnectionStates1, Hashes1} =
+ encode_handshake(Verified, SigAlg, Version,
+ ConnectionStates0, Hashes0),
+ Transport:send(Socket, BinVerified),
+ State#state{connection_states = ConnectionStates1,
+ tls_handshake_hashes = Hashes1}
+ end;
+verify_client_cert(#state{client_certificate_requested = false} = State) ->
+ State.
+
+do_server_hello(Type, #state{negotiated_version = Version,
+ session = Session,
+ connection_states = ConnectionStates0}
+ = State0) when is_atom(Type) ->
+ ServerHello =
+ ssl_handshake:server_hello(Session#session.session_id, Version,
+ ConnectionStates0),
+ State = server_hello(ServerHello, State0),
+
+ case Type of
+ new ->
+ do_server_hello(ServerHello, State);
+ resumed ->
+ case ssl_handshake:master_secret(Version, Session,
+ ConnectionStates0, server) of
+ {_, ConnectionStates1} ->
+ {ConnectionStates, Hashes} =
+ finished(State#state{connection_states =
+ ConnectionStates1}),
+ {next_state, abbreviated,
+ next_record(State#state{connection_states =
+ ConnectionStates,
+ tls_handshake_hashes = Hashes})};
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, hello, State),
+ {stop, normal, State}
+ end
+ end;
+
+do_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),
+ Session =
+ Session0#session{session_id = SessionId,
+ cipher_suite = CipherSuite,
+ compression_method = Compression},
+ {next_state, certify, State#state{session = Session}}
+ catch
+ #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,
+ %% Reinitialize
+ client_certificate_requested = false,
+ tls_handshake_hashes = Hashes},
+ {next_state, cipher, next_record(State)}
+
+ catch
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, certify_foo, State0),
+ {stop, normal, State0}
+ end.
+
+do_client_certify_and_key_exchange(State0) ->
+ State1 = certify_client(State0),
+ State2 = key_exchange(State1),
+ verify_client_cert(State2).
+
+server_certify_and_key_exchange(State0) ->
+ State1 = certify_server(State0),
+ State2 = key_exchange(State1),
+ request_client_cert(State2).
+
+server_hello(ServerHello, #state{transport_cb = Transport,
+ socket = Socket,
+ negotiated_version = Version,
+ 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
+ {BinMsg, ConnectionStates1, Hashes1} =
+ encode_handshake(ServerHello, Version, ConnectionStates0, Hashes0),
+ Transport:send(Socket, BinMsg),
+ State#state{connection_states = ConnectionStates1,
+ tls_handshake_hashes = Hashes1,
+ key_algorithm = KeyAlgorithm}.
+
+server_hello_done(#state{transport_cb = Transport,
+ socket = Socket,
+ negotiated_version = Version,
+ connection_states = ConnectionStates,
+ tls_handshake_hashes = Hashes} = State0) ->
+
+ 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) ->
+
+ case ssl_handshake:certificate(OwnCert, CertDbRef, server) of
+ CertMsg = #certificate{} ->
+ {BinCertMsg, NewConnectionStates, NewHashes} =
+ encode_handshake(CertMsg, Version, ConnectionStates, Hashes),
+ Transport:send(Socket, BinCertMsg),
+ State#state{connection_states = NewConnectionStates,
+ tls_handshake_hashes = NewHashes
+ };
+ Alert = #alert{} ->
+ throw(Alert)
+ end.
+
+key_exchange(#state{role = server, key_algorithm = Algo} = State)
+ when Algo == rsa;
+ Algo == dh_dss;
+ Algo == dh_rsa ->
+ 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,
+ connection_states = ConnectionStates0,
+ negotiated_version = Version,
+ tls_handshake_hashes = Hashes0,
+ socket = Socket,
+ transport_cb = Transport
+ } = State)
+ when Algo == dhe_dss;
+ Algo == dhe_dss_export;
+ Algo == dhe_rsa;
+ Algo == dhe_rsa_export ->
+ Msg = ssl_handshake:key_exchange(server, Params),
+ {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 = server, key_algorithm = dh_anon,
+ connection_states = ConnectionStates0,
+ negotiated_version = Version,
+ tls_handshake_hashes = Hashes0,
+ socket = Socket,
+ transport_cb = Transport
+ } = State) ->
+ Msg = ssl_handshake:key_exchange(server, anonymous),
+ {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 = rsa,
+ public_key_info = PublicKeyInfo,
+ negotiated_version = Version,
+ premaster_secret = PremasterSecret,
+ socket = Socket, transport_cb = Transport,
+ tls_handshake_hashes = Hashes0} = State) ->
+ Msg = rsa_key_exchange(PremasterSecret, PublicKeyInfo),
+ {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,
+ public_key_info = PublicKeyInfo,
+ negotiated_version = Version,
+ diffie_hellman_params = Params,
+ own_cert = Cert,
+ 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 ->
+ Msg = dh_key_exchange(Cert, Params, PublicKeyInfo),
+ {BinMsg, ConnectionStates1, Hashes1} =
+ encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ Transport:send(Socket, BinMsg),
+ State#state{connection_states = ConnectionStates1,
+ tls_handshake_hashes = Hashes1}.
+
+rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
+ when Algorithm == ?rsaEncryption;
+ Algorithm == ?md2WithRSAEncryption;
+ Algorithm == ?md5WithRSAEncryption;
+ Algorithm == ?sha1WithRSAEncryption ->
+ ssl_handshake:key_exchange(client,
+ {premaster_secret, PremasterSecret,
+ PublicKeyInfo});
+
+rsa_key_exchange(_, _) ->
+ throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)).
+
+dh_key_exchange(OwnCert, Params, PublicKeyInfo) ->
+ case public_key:pkix_is_fixed_dh_cert(OwnCert) of
+ true ->
+ ssl_handshake:key_exchange(client, fixed_diffie_hellman);
+ false ->
+ ssl_handshake:key_exchange(client, {dh, Params, PublicKeyInfo})
+ end.
+
+request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer},
+ connection_states = ConnectionStates0,
+ cert_db_ref = CertDbRef,
+ tls_handshake_hashes = Hashes0,
+ negotiated_version = Version,
+ socket = Socket,
+ transport_cb = Transport} = State) ->
+ Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbRef),
+ {BinMsg, ConnectionStates1, Hashes1} =
+ encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ Transport:send(Socket, BinMsg),
+ State#state{client_certificate_requested = true,
+ connection_states = ConnectionStates1,
+ tls_handshake_hashes = Hashes1};
+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,
+ write),
+ finished(State#state{connection_states = ConnectionStates2}).
+
+
+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,
+ socket = Socket,
+ negotiated_version = Version,
+ transport_cb = Transport}) ->
+ {BinChangeCipher, NewConnectionStates} =
+ encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates),
+ Transport:send(Socket, BinChangeCipher),
+ NewConnectionStates.
+
+finished(#state{role = Role, socket = Socket, negotiated_version = Version,
+ transport_cb = Transport,
+ session = Session,
+ connection_states = ConnectionStates,
+ tls_handshake_hashes = Hashes}) ->
+ MasterSecret = Session#session.master_secret,
+ Finished = ssl_handshake:finished(Version, Role, MasterSecret, Hashes),
+ {BinFinished, NewConnectionStates, NewHashes} =
+ encode_handshake(Finished, Version, ConnectionStates, Hashes),
+ Transport:send(Socket, BinFinished),
+ {NewConnectionStates, NewHashes}.
+
+handle_server_key(_KeyExchangeMsg, State) ->
+ State.
+handle_clinet_key(_KeyExchangeMsg, State) ->
+ State.
+
+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),
+ Hashes1 = ssl_handshake:update_hashes(Hashes0, Frag),
+ {E, ConnectionStates1} =
+ ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
+ {E, ConnectionStates1, Hashes1}.
+
+encode_data(Data, Version, ConnectionStates) ->
+ ssl_record:encode_data(Data, Version, ConnectionStates).
+
+decode_alerts(Bin) ->
+ decode_alerts(Bin, []).
+
+decode_alerts(<<?BYTE(Level), ?BYTE(Description), Rest/binary>>, Acc) ->
+ A = ?ALERT_REC(Level, Description),
+ decode_alerts(Rest, [A | Acc]);
+decode_alerts(<<>>, Acc) ->
+ lists:reverse(Acc, []).
+
+application_data(Data, #state{user_application = {_Mon, Pid},
+ socket_options = SOpts,
+ bytes_to_read = BytesToRead,
+ from = From,
+ user_data_buffer = Buffer0} = State0) ->
+ Buffer1 = if
+ Buffer0 =:= <<>> -> Data;
+ Data =:= <<>> -> Buffer0;
+ 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,
+ from = undefined,
+ bytes_to_read = 0,
+ 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)
+ end;
+ {error,_Reason} -> %% Invalid packet in packet mode
+ deliver_packet_error(SOpts, Buffer1, Pid, From),
+ {stop, normal, State0}
+ end.
+
+%% Picks ClientData
+get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer)
+ when Raw =:= raw; Raw =:= 0 -> %% Raw Mode
+ if
+ Active =/= false orelse BytesToRead =:= 0 ->
+ %% Active true or once, or passive mode recv(0)
+ {ok, Buffer, <<>>};
+ byte_size(Buffer) >= BytesToRead ->
+ %% Passive Mode, recv(Bytes)
+ <<Data:BytesToRead/binary, Rest/binary>> = Buffer,
+ {ok, Data, Rest};
+ true ->
+ %% Passive Mode not enough data
+ {ok, <<>>, Buffer}
+ end;
+get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) ->
+ PacketOpts = [{packet_size, Size}],
+ case erlang:decode_packet(Type, Buffer, PacketOpts) of
+ {more, _} ->
+ {ok, <<>>, Buffer};
+ Decoded ->
+ Decoded
+ end.
+
+deliver_app_data(SO = #socket_options{active=once}, Data, Pid, From) ->
+ send_or_reply(once, Pid, From, format_reply(SO, Data)),
+ SO#socket_options{active=false};
+deliver_app_data(SO= #socket_options{active=Active}, Data, Pid, From) ->
+ send_or_reply(Active, Pid, From, format_reply(SO, Data)),
+ SO.
+
+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)}.
+
+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) ->
+ gen_fsm:reply(From, Data);
+send_or_reply(_, Pid, _From, Data) ->
+ send_user(Pid, Data).
+
+opposite_role(client) ->
+ server;
+opposite_role(server) ->
+ client.
+
+send_user(Pid, Msg) ->
+ Pid ! Msg.
+
+%% %% This is the code for {packet,ssl} removed because it was slower
+%% %% than handling it in erlang.
+%% next_record(#state{socket = Socket,
+%% tls_buffer = [Msg|Rest],
+%% connection_states = ConnectionStates0} = State) ->
+%% Buffer =
+%% case Rest of
+%% [] ->
+%% inet:setopts(Socket, [{active,once}]),
+%% buffer;
+%% _ -> Rest
+%% end,
+%% case Msg of
+%% #ssl_tls{} ->
+%% {Plain, ConnectionStates} =
+%% ssl_record:decode_cipher_text(Msg, ConnectionStates0),
+%% gen_fsm:send_all_state_event(self(), Plain),
+%% State#state{tls_buffer=Buffer, connection_states = ConnectionStates};
+%% {ssl_close, Msg} ->
+%% self() ! Msg,
+%% State#state{tls_buffer=Buffer}
+%% end;
+%% next_record(#state{socket = Socket, tls_buffer = undefined} = State) ->
+%% inet:setopts(Socket, [{active,once}]),
+%% State#state{tls_buffer=continue};
+%% next_record(State) ->
+%% State#state{tls_buffer=continue}.
+
+next_record(#state{tls_cipher_texts = [], socket = Socket} = State) ->
+ inet:setopts(Socket, [{active,once}]),
+ State;
+next_record(#state{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}.
+
+next_record_if_active(State =
+ #state{socket_options =
+ #socket_options{active = false}}) ->
+ State;
+next_record_if_active(State) ->
+ next_record(State).
+
+register_session(_, _, _, #session{is_resumable = true} = Session) ->
+ Session; %% Already registered
+register_session(client, Host, Port, Session0) ->
+ Session = Session0#session{is_resumable = true},
+ ssl_manager:register_session(Host, Port, Session),
+ Session;
+register_session(server, _, Port, Session0) ->
+ Session = Session0#session{is_resumable = true},
+ ssl_manager:register_session(Port, Session),
+ Session.
+
+invalidate_session(client, Host, Port, Session) ->
+ ssl_manager:invalidate_session(Host, Port, Session);
+invalidate_session(server, _, Port, Session) ->
+ ssl_manager:invalidate_session(Port, Session).
+
+initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
+ {CbModule, DataTag, CloseTag}) ->
+ ConnectionStates = ssl_record:init_connection_states(Role),
+
+ SessionCacheCb = case application:get_env(ssl, session_cb) of
+ {ok, Cb} when is_atom(Cb) ->
+ Cb;
+ _ ->
+ ssl_session_cache
+ end,
+
+ Monitor = erlang:monitor(process, User),
+
+ #state{socket_options = SocketOptions,
+ %% We do not want to save the password in the state so that
+ %% could be written in the clear into error logs.
+ ssl_options = SSLOptions#ssl_options{password = undefined},
+ session = #session{is_resumable = false},
+ transport_cb = CbModule,
+ data_tag = DataTag,
+ close_tag = CloseTag,
+ role = Role,
+ host = Host,
+ port = Port,
+ socket = Socket,
+ connection_states = ConnectionStates,
+ tls_handshake_buffer = <<>>,
+ tls_record_buffer = <<>>,
+ tls_cipher_texts = [],
+ user_application = {Monitor, User},
+ bytes_to_read = 0,
+ user_data_buffer = <<>>,
+ log_alert = true,
+ session_cache_cb = SessionCacheCb
+ }.
+
+sslsocket(Pid) ->
+ #sslsocket{pid = Pid, fd = new_ssl}.
+
+sslsocket() ->
+ sslsocket(self()).
+
+get_socket_opts(_,[], _, Acc) ->
+ {ok, Acc};
+get_socket_opts(Socket, [mode | Tags], SockOpts, Acc) ->
+ get_socket_opts(Socket, Tags, SockOpts,
+ [{mode, SockOpts#socket_options.mode} | Acc]);
+get_socket_opts(Socket, [packet | Tags], SockOpts, Acc) ->
+ get_socket_opts(Socket, Tags, SockOpts,
+ [{packet, SockOpts#socket_options.packet} | Acc]);
+get_socket_opts(Socket, [header | Tags], SockOpts, Acc) ->
+ get_socket_opts(Socket, Tags, SockOpts,
+ [{header, SockOpts#socket_options.header} | Acc]);
+get_socket_opts(Socket, [active | Tags], SockOpts, Acc) ->
+ get_socket_opts(Socket, Tags, SockOpts,
+ [{active, SockOpts#socket_options.active} | Acc]);
+get_socket_opts(Socket, [Tag | Tags], SockOpts, Acc) ->
+ case inet:getopts(Socket, [Tag]) of
+ {ok, [Opt]} ->
+ get_socket_opts(Socket, Tags, SockOpts, [Opt | Acc]);
+ {error, Error} ->
+ {error, Error}
+ end.
+
+set_socket_opts(_, [], SockOpts, []) ->
+ SockOpts;
+set_socket_opts(Socket, [], SockOpts, Other) ->
+ %% Set non emulated options
+ inet:setopts(Socket, Other),
+ SockOpts;
+set_socket_opts(Socket, [{mode, Mode}| Opts], SockOpts, Other) ->
+ set_socket_opts(Socket, Opts, SockOpts#socket_options{mode = Mode}, Other);
+set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) ->
+ set_socket_opts(Socket, Opts,
+ SockOpts#socket_options{packet = Packet}, Other);
+set_socket_opts(Socket, [{header, Header}| Opts], SockOpts, Other) ->
+ set_socket_opts(Socket, Opts,
+ SockOpts#socket_options{header = Header}, Other);
+set_socket_opts(Socket, [{active, Active}| Opts], SockOpts, Other) ->
+ set_socket_opts(Socket, Opts,
+ SockOpts#socket_options{active = Active}, Other);
+set_socket_opts(Socket, [Opt | Opts], SockOpts, Other) ->
+ set_socket_opts(Socket, Opts, SockOpts, [Opt | Other]).
+
+alert_user(From, Alert, Role) ->
+ alert_user(false, no_pid, From, Alert, Role).
+
+alert_user(false = Active, Pid, From, Alert, Role) ->
+ ReasonCode = ssl_alert:reason_code(Alert, Role),
+ send_or_reply(Active, Pid, From, {error, ReasonCode});
+alert_user(Active, Pid, From, Alert, Role) ->
+ case ssl_alert:reason_code(Alert, Role) of
+ closed ->
+ send_or_reply(Active, Pid, From,
+ {ssl_closed, sslsocket()});
+ ReasonCode ->
+ send_or_reply(Active, Pid, From,
+ {ssl_error, sslsocket(), ReasonCode})
+ end.
+
+log_alert(true, StateName, Alert) ->
+ Txt = ssl_alert:alert_txt(Alert),
+ error_logger:format("SSL: ~p: ~s\n", [StateName, Txt]);
+log_alert(false, _, _) ->
+ ok.
+
+handle_own_alert(Alert, Version, StateName,
+ #state{transport_cb = Transport,
+ socket = Socket,
+ from = User,
+ role = Role,
+ connection_states = ConnectionStates,
+ log_alert = Log}) ->
+ {BinMsg, _} =
+ encode_alert(Alert, Version, ConnectionStates),
+ Transport:send(Socket, BinMsg),
+ log_alert(Log, StateName, Alert),
+ alert_user(User, Alert, Role).
+
+make_premaster_secret({MajVer, MinVer}) ->
+ Rand = crypto:rand_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
+ <<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>.
diff --git a/lib/ssl/src/ssl_connection_sup.erl b/lib/ssl/src/ssl_connection_sup.erl
new file mode 100644
index 0000000000..e9328d5f7c
--- /dev/null
+++ b/lib/ssl/src/ssl_connection_sup.erl
@@ -0,0 +1,60 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: The top supervisor for the ftp hangs under inets_sup.
+%%----------------------------------------------------------------------
+-module(ssl_connection_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+-export([start_child/1]).
+
+%% Supervisor callback
+-export([init/1]).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+start_child(Args) ->
+ supervisor:start_child(?MODULE, Args).
+
+%%%=========================================================================
+%%% Supervisor callback
+%%%=========================================================================
+init(_O) ->
+ RestartStrategy = simple_one_for_one,
+ MaxR = 0,
+ MaxT = 3600,
+
+ Name = undefined, % As simple_one_for_one is used.
+ StartFunc = {ssl_connection, start_link, []},
+ Restart = temporary, % E.g. should not be restarted
+ Shutdown = 4000,
+ Modules = [ssl_connection],
+ Type = worker,
+
+ ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules},
+ {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}.
diff --git a/lib/ssl/src/ssl_debug.erl b/lib/ssl/src/ssl_debug.erl
new file mode 100644
index 0000000000..625889c43b
--- /dev/null
+++ b/lib/ssl/src/ssl_debug.erl
@@ -0,0 +1,99 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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 : some debug utilities
+
+-module(ssl_debug).
+
+-export([unhex/1, hexd/1, hex_data/2, term_data/2, hex_data/4, term_data/4, make_binary/1]).
+
+%% external
+
+hex_data(Name, Data) ->
+ io:format("~s\n~s", [Name, hex(Data)]).
+
+term_data(Name, Term) ->
+ io:format("~s\n~p\n", [Name, Term]).
+
+hex_data(Name, Data, Mod, Line) ->
+ io:format("~w:~p ~s\n~s", [Mod, Line, Name, hex(Data)]).
+
+term_data(Name, Term, Mod, Line) ->
+ io:format("~w:~p ~s\n~p\n", [Mod, Line, Name, Term]).
+
+unhex(S) ->
+ Lines = string:tokens(S, "\n"),
+ H = [unhex(L, []) || L <- Lines],
+ list_to_binary(H).
+
+make_binary(Size) ->
+ crypto:rand_bytes(Size).
+
+%% internal
+
+is_hex_digit(C) when C >= $0, C =< $9 -> true;
+is_hex_digit(C) when C >= $A, C =< $F -> true;
+is_hex_digit(C) when C >= $a, C =< $f -> true;
+is_hex_digit(_) -> false.
+
+unhex([], Acc) ->
+ list_to_binary(lists:reverse(Acc));
+unhex([_], Acc) ->
+ unhex([], Acc);
+unhex([$ | Tl], Acc) ->
+ unhex(Tl, Acc);
+unhex([D1, D2 | Tl], Acc) ->
+ case {is_hex_digit(D1), is_hex_digit(D2)} of
+ {true, true} ->
+ unhex(Tl, [erlang:list_to_integer([D1, D2], 16) | Acc]);
+ _ ->
+ unhex([], Acc)
+ end.
+
+hexd(B) ->
+ io:format("~s\n", [hex(B)]).
+
+hex(B) -> hex(erlang:iolist_to_binary(B), []).
+
+hex_asc(B) ->
+ L = binary_to_list(B),
+ {hexify(L), asciify(L)}.
+
+hex(<<B:16/binary, Rest/binary>>, Acc) ->
+ {HS, AS} = hex_asc(B),
+ hex(Rest, ["\n", AS, " ", HS | Acc]);
+hex(<<>>, Acc) ->
+ lists:reverse(Acc);
+hex(B, Acc) ->
+ {HS, AS} = hex_asc(B),
+ L = erlang:iolist_size(HS),
+ lists:flatten(lists:reverse(Acc, [HS, lists:duplicate(3*16 - L, $ ), " ", AS, "\n"])).
+
+hexify(L) -> [[hex_byte(B), " "] || B <- L].
+
+hex_byte(B) when B < 16#10 -> ["0", erlang:integer_to_list(B, 16)];
+hex_byte(B) -> erlang:integer_to_list(B, 16).
+
+asciify(L) -> [ascii_byte(C) || C <- L].
+
+ascii_byte($") -> $.;
+ascii_byte(C) when C < 32; C >= 127 -> $.;
+ascii_byte(C) -> C.
diff --git a/lib/ssl/src/ssl_debug.hrl b/lib/ssl/src/ssl_debug.hrl
new file mode 100644
index 0000000000..e88cef441f
--- /dev/null
+++ b/lib/ssl/src/ssl_debug.hrl
@@ -0,0 +1,39 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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_debug).
+-define(ssl_debug, true).
+
+-ifdef(SSL_DEBUG).
+-define(DBG_HEX(V), ssl_debug:hex_data(??V, V, ?MODULE, ?LINE)).
+-define(DBG_TERM(T), ssl_debug:term_data(??T, T, ?MODULE, ?LINE)).
+-else.
+-define(DBG_HEX(V), ok).
+-define(DBG_TERM(T), ok).
+-endif.
+
+-endif. % -ifdef(ssl_debug).
+
+
+
+
+
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
new file mode 100644
index 0000000000..829e0c2ba6
--- /dev/null
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -0,0 +1,917 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Help funtions for handling the SSL-handshake protocol
+%%----------------------------------------------------------------------
+
+-module(ssl_handshake).
+
+-include("ssl_handshake.hrl").
+-include("ssl_record.hrl").
+-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,
+ certify/5, certificate/3,
+ client_certificate_verify/6,
+ certificate_verify/6, certificate_request/2,
+ key_exchange/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]).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: client_hello(Host, Port, ConnectionStates, SslOpts) ->
+%% #client_hello{}
+%% Host
+%% Port
+%% ConnectionStates = #connection_states{}
+%% SslOpts = #ssl_options{}
+%%
+%% Description: Creates a client hello message.
+%%--------------------------------------------------------------------
+client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
+ ciphers = Ciphers}
+ = SslOpts) ->
+
+ Fun = fun(Version) ->
+ ssl_record:protocol_version(Version)
+ end,
+ Version = ssl_record:highest_protocol_version(lists:map(Fun, Versions)),
+ Pending = ssl_record:pending_connection_state(ConnectionStates, read),
+ SecParams = Pending#connection_state.security_parameters,
+
+ Id = ssl_manager:client_session_id(Host, Port, SslOpts),
+
+ #client_hello{session_id = Id,
+ client_version = Version,
+ cipher_suites = Ciphers,
+ compression_methods = ssl_record:compressions(),
+ random = SecParams#security_parameters.client_random
+ }.
+
+%%--------------------------------------------------------------------
+%% Function: server_hello(Host, Port, SessionId,
+%% Version, ConnectionStates) -> #server_hello{}
+%% SessionId
+%% Version
+%% ConnectionStates
+%%
+%%
+%% Description: Creates a server hello message.
+%%--------------------------------------------------------------------
+server_hello(SessionId, Version, ConnectionStates) ->
+ Pending = ssl_record:pending_connection_state(ConnectionStates, read),
+ SecParams = Pending#connection_state.security_parameters,
+ #server_hello{server_version = Version,
+ cipher_suite = SecParams#security_parameters.cipher_suite,
+ compression_method =
+ SecParams#security_parameters.compression_algorithm,
+ random = SecParams#security_parameters.server_random,
+ session_id = SessionId
+ }.
+
+%%--------------------------------------------------------------------
+%% Function: hello(Hello, Info) ->
+%% {Version, Id, NewConnectionStates} |
+%% #alert{}
+%%
+%% Hello = #client_hello{} | #server_hello{}
+%% Info = ConnectionStates | {Port, Session, ConnectionStates}
+%% ConnectionStates = #connection_states{}
+%%
+%% 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}) ->
+ 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),
+ case CipherSuite of
+ no_suite ->
+ ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
+ _ ->
+ ConnectionStates =
+ hello_pending_connection_states(server,
+ CipherSuite,
+ Random,
+ Compression,
+ ConnectionStates0),
+ {Version, {Type, Session}, ConnectionStates}
+ end;
+ false ->
+ ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: certify(Certs, CertDbRef, MaxPathLen) ->
+%% {PeerCert, PublicKeyInfo} | #alert{}
+%%
+%% Certs = #certificate{}
+%% CertDbRef = reference()
+%% MaxPathLen = integer() | nolimit
+%%
+%% Description: Handles a certificate handshake message
+%%--------------------------------------------------------------------
+certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef,
+ MaxPathLen, Verify, VerifyFun) ->
+ [PeerCert | _] = ASN1Certs,
+ VerifyBool = verify_bool(Verify),
+
+ 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},
+ {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
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: certificate(OwnCert, CertDbRef, Role) -> #certificate{}
+%%
+%% OwnCert = binary()
+%% CertDbRef = term() as returned by ssl_certificate_db:create()
+%%
+%% Description: Creates a certificate message.
+%%--------------------------------------------------------------------
+certificate(OwnCert, CertDbRef, client) ->
+ Chain =
+ case ssl_certificate:certificate_chain(OwnCert, CertDbRef) of
+ {ok, CertChain} ->
+ CertChain;
+ {error, _} ->
+ %% If no suitable certificate is available, the client
+ %% SHOULD send a certificate message containing no
+ %% certificates. (chapter 7.4.6. rfc 4346)
+ []
+ end,
+ #certificate{asn1_certificates = Chain};
+
+certificate(OwnCert, CertDbRef, server) ->
+ case ssl_certificate:certificate_chain(OwnCert, CertDbRef) of
+ {ok, Chain} ->
+ #certificate{asn1_certificates = Chain};
+ {error, _} ->
+ ?ALERT_REC(?FATAL, ?INTERNAL_ERROR)
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: client_certificate_verify(Cert, ConnectionStates) ->
+%% #certificate_verify{} | ignore
+%% Cert = #'OTPcertificate'{}
+%% ConnectionStates = #connection_states{}
+%%
+%% Description: Creates a certificate_verify message, called by the client.
+%%--------------------------------------------------------------------
+client_certificate_verify(undefined, _, _, _, _, _) ->
+ ignore;
+client_certificate_verify(_, _, _, _, undefined, _) ->
+ ignore;
+client_certificate_verify(OwnCert, MasterSecret, Version, Algorithm,
+ PrivateKey, {Hashes0, _}) ->
+ case public_key:pkix_is_fixed_dh_cert(OwnCert) of
+ true ->
+ ignore;
+ false ->
+ Hashes =
+ calc_certificate_verify(Version, MasterSecret,
+ Algorithm, Hashes0),
+ Signed = digitally_signed(Hashes, PrivateKey),
+ #certificate_verify{signature = Signed}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: certificate_verify(Signature, PublicKeyInfo) -> valid | #alert{}
+%%
+%% Signature = binary()
+%% PublicKeyInfo = {Algorithm, PublicKey, PublicKeyParams}
+%%
+%% 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 ->
+ Hashes = calc_certificate_verify(Version, MasterSecret,
+ Algorithm, Hashes0),
+ case public_key:decrypt_public(Signature, PublicKey,
+ [{rsa_pad, rsa_pkcs1_padding}]) of
+ Hashes ->
+ valid;
+ _ ->
+ ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
+ end.
+%% TODO dsa clause
+
+%%--------------------------------------------------------------------
+%% Function: certificate_request(ConnectionStates, CertDbRef) ->
+%% #certificate_request{}
+%%
+%% Description: Creates a certificate_request message, called by the server.
+%%--------------------------------------------------------------------
+certificate_request(ConnectionStates, CertDbRef) ->
+ #connection_state{security_parameters =
+ #security_parameters{cipher_suite = CipherSuite}} =
+ ssl_record:pending_connection_state(ConnectionStates, read),
+ Types = certificate_types(CipherSuite),
+ Authorities = certificate_authorities(CertDbRef),
+ #certificate_request{
+ certificate_types = Types,
+ certificate_authorities = Authorities
+ }.
+
+%%--------------------------------------------------------------------
+%% Function: key_exchange(Role, Secret, Params) ->
+%% #client_key_exchange{} | #server_key_exchange{}
+%%
+%% Secret -
+%% Params -
+%%
+%% Description: Creates a keyexchange message.
+%%--------------------------------------------------------------------
+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, PublicKey}) ->
+ Len = byte_size(PublicKey),
+ #client_key_exchange{
+ exchange_keys = #client_diffie_hellman_public{
+ dh_public = <<?UINT16(Len), PublicKey/binary>>}
+ };
+
+%% key_exchange(server, {{?'dhpublicnumber', _PublicKey,
+%% #'DomainParameters'{p = P, g = G, y = Y},
+%% SignAlgorithm, ClientRandom, ServerRandom}}) ->
+%% ServerDHParams = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
+%% PLen = byte_size(P),
+%% GLen = byte_size(G),
+%% YLen = byte_size(Y),
+%% Hash = server_key_exchange_hash(SignAlgorithm, <<ClientRandom/binary,
+%% ServerRandom/binary,
+%% ?UINT16(PLen), P/binary,
+%% ?UINT16(GLen), G/binary,
+%% ?UINT16(YLen), Y/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
+%%
+%% 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.
+%%-------------------------------------------------------------------
+master_secret(Version, #session{master_secret = Mastersecret},
+ ConnectionStates, Role) ->
+ ConnectionState =
+ ssl_record:pending_connection_state(ConnectionStates, read),
+ SecParams = ConnectionState#connection_state.security_parameters,
+ try master_secret(Version, Mastersecret, SecParams,
+ ConnectionStates, Role)
+ catch
+ exit:Reason ->
+ error_logger:error_report("Key calculation failed due to ~p",
+ [Reason]),
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
+ end;
+
+master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
+ ConnectionState =
+ ssl_record:pending_connection_state(ConnectionStates, read),
+ SecParams = ConnectionState#connection_state.security_parameters,
+ #security_parameters{client_random = ClientRandom,
+ server_random = ServerRandom} = SecParams,
+ try master_secret(Version,
+ calc_master_secret(Version,PremasterSecret,
+ ClientRandom, ServerRandom),
+ SecParams, ConnectionStates, Role)
+ catch
+ exit:Reason ->
+ error_logger:error_report("Master secret calculation failed"
+ " due to ~p", [Reason]),
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: finished(Version, Role, MacSecret, Hashes) -> #finished{}
+%%
+%% ConnectionStates = #connection_states{}
+%%
+%% Description: Creates a handshake finished message
+%%-------------------------------------------------------------------
+finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes
+ #finished{verify_data =
+ 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}
+%%
+%%
+%% Description: Checks the ssl handshake finished message to verify
+%% the connection.
+%%-------------------------------------------------------------------
+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)
+ end.
+
+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{}
+%%
+%% encode a handshake packet to binary
+%%--------------------------------------------------------------------
+encode_handshake(Package, Version, SigAlg) ->
+ {MsgType, Bin} = enc_hs(Package, Version, SigAlg),
+ Len = byte_size(Bin),
+ [MsgType, ?uint24(Len), Bin].
+
+%%--------------------------------------------------------------------
+%% Function: get_tls_handshake(Data, Buffer) -> Result
+%% Result = {[#handshake{}], [Raw], NewBuffer}
+%% Data = Buffer = NewBuffer = Raw = binary()
+%%
+%% Description: Given buffered and new data from ssl_record, collects
+%% and returns it as a list of #handshake, 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_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, KeyAlg, Version),
+ get_tls_handshake_aux(Rest, KeyAlg, Version, [{H,Raw} | Acc]);
+get_tls_handshake_aux(Data, _KeyAlg, _Version, Acc) ->
+ {lists:reverse(Acc), Data}.
+
+%%--------------------------------------------------------------------
+%% Function: sig_alg(atom()) -> integer()
+%%
+%% Description: Convert from key exchange as atom to signature
+%% algorithm as a ?SIGNATURE_... constant
+%%--------------------------------------------------------------------
+
+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.
+
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+verify_bool(verify_peer) ->
+ true;
+verify_bool(verify_none) ->
+ false.
+
+path_validation_alert({bad_cert, cert_expired}, _) ->
+ ?ALERT_REC(?FATAL, ?CERTIFICATE_EXPIRED);
+path_validation_alert({bad_cert, invalid_issuer}, _) ->
+ ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
+path_validation_alert({bad_cert, invalid_signature} , _) ->
+ ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
+path_validation_alert({bad_cert, name_not_permitted}, _) ->
+ ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
+path_validation_alert({bad_cert, unknown_critical_extension}, _) ->
+ ?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE);
+path_validation_alert({bad_cert, cert_revoked}, _) ->
+ ?ALERT_REC(?FATAL, ?CERTIFICATE_REVOKED);
+path_validation_alert(_, _) ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE).
+
+select_session(Hello, Port, Session, Version,
+ #ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb) ->
+ 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,
+
+ case ssl_session:is_new(SuggestedSessionId, SessionId) of
+ true ->
+ CipherSuite =
+ select_cipher_suite(Hello#client_hello.cipher_suites, Suites),
+ Compressions = Hello#client_hello.compression_methods,
+ Compression = select_compression(Compressions),
+ {new, Session#session{session_id = SessionId,
+ cipher_suite = CipherSuite,
+ compression_method = Compression}};
+ false ->
+ {resumed, CacheCb:lookup(Cache, {Port, SessionId})}
+ end.
+
+%% Update pending connection states with parameters exchanged via
+%% hello messages
+%% NOTE : Role is the role of the receiver of the hello message
+%% currently being processed.
+hello_pending_connection_states(Role, CipherSuite, Random, Compression,
+ ConnectionStates) ->
+ ReadState =
+ ssl_record:pending_connection_state(ConnectionStates, read),
+ WriteState =
+ ssl_record:pending_connection_state(ConnectionStates, write),
+
+ NewReadSecParams =
+ hello_security_parameters(Role, ReadState, CipherSuite,
+ Random, Compression),
+
+ NewWriteSecParams =
+ hello_security_parameters(Role, WriteState, CipherSuite,
+ Random, Compression),
+
+ ssl_record:update_security_params(NewReadSecParams,
+ NewWriteSecParams,
+ ConnectionStates).
+
+hello_security_parameters(client, ConnectionState, CipherSuite, Random,
+ Compression) ->
+ SecParams = ConnectionState#connection_state.security_parameters,
+ NewSecParams = ssl_cipher:security_parameters(CipherSuite, SecParams),
+ NewSecParams#security_parameters{
+ server_random = Random,
+ compression_algorithm = Compression
+ };
+
+hello_security_parameters(server, ConnectionState, CipherSuite, Random,
+ Compression) ->
+ SecParams = ConnectionState#connection_state.security_parameters,
+ NewSecParams = ssl_cipher:security_parameters(CipherSuite, SecParams),
+ NewSecParams#security_parameters{
+ client_random = Random,
+ compression_algorithm = Compression
+ }.
+
+select_version(ClientVersion, Versions) ->
+ Fun = fun(Version) ->
+ ssl_record:protocol_version(Version)
+ end,
+ ServerVersion = ssl_record:highest_protocol_version(lists:map(Fun,
+ Versions)),
+ ssl_record:lowest_protocol_version(ClientVersion, ServerVersion).
+
+select_cipher_suite([], _) ->
+ no_suite;
+select_cipher_suite([Suite | ClientSuites], SupportedSuites) ->
+ case is_member(Suite, SupportedSuites) of
+ true ->
+ Suite;
+ false ->
+ select_cipher_suite(ClientSuites, SupportedSuites)
+ end.
+
+is_member(Suite, SupportedSuites) ->
+ lists:member(Suite, SupportedSuites).
+
+select_compression(_CompressionMetodes) ->
+ ?NULL.
+
+master_secret(Version, MasterSecret, #security_parameters{
+ client_random = ClientRandom,
+ server_random = ServerRandom,
+ hash_size = HashSize,
+ key_material_length = KML,
+ expanded_key_material_length = EKML,
+ iv_size = IVS,
+ exportable = Exportable},
+ ConnectionStates, Role) ->
+ {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
+ ServerWriteKey, ClientIV, ServerIV} =
+ setup_keys(Version, Exportable, 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,
+ Role, ConnStates1),
+
+ ClientCipherState = #cipher_state{iv = ClientIV, key = ClientWriteKey},
+ ServerCipherState = #cipher_state{iv = ServerIV, key = ServerWriteKey},
+ {MasterSecret,
+ ssl_record:set_pending_cipher_state(ConnStates2, ClientCipherState,
+ ServerCipherState, Role)}.
+
+
+dec_hs(?HELLO_REQUEST, <<>>, _, _) ->
+ #hello_request{};
+
+%% Client hello v2.
+%% The server must be able to receive such messages, from clients that
+%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
+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),
+ #client_hello{client_version = {Major, Minor},
+ random = ssl_ssl2:client_random(ChallengeData, CDLength),
+ session_id = 0,
+ cipher_suites = from_3bytes(CipherSuites),
+ compression_methods = [?NULL]
+ };
+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>>,
+ _, _) ->
+ #client_hello{
+ client_version = {Major,Minor},
+ random = Random,
+ session_id = Session_ID,
+ cipher_suites = from_2bytes(CipherSuites),
+ compression_methods = Comp_methods
+ };
+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)>>, _, _) ->
+ #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>>, _, _) ->
+ #certificate{asn1_certificates = certs_to_list(ASN1Certs)};
+dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod:ModLen/binary,
+ ?UINT16(ExpLen), Exp:ExpLen/binary,
+ 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,
+ Sig/binary>>,
+ ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
+ #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?
+ #certificate_request{certificate_types = CertTypes,
+ certificate_authorities = CertAuths};
+dec_hs(?SERVER_HELLO_DONE, <<>>, _, _) ->
+ #server_hello_done{};
+dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>, _, _)->
+ #certificate_verify{signature = Signature};
+dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS, rsa, {3, 0}) ->
+ PreSecret = #encrypted_premaster_secret{premaster_secret = PKEPMS},
+ #client_key_exchange{exchange_keys = PreSecret};
+dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(_), PKEPMS/binary>>, 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_YCLen), DH_YC:DH_YCLen/binary>>,
+ ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
+ #client_diffie_hellman_public{dh_public = DH_YC};
+dec_hs(?FINISHED, VerifyData, _, _) ->
+ #finished{verify_data = VerifyData};
+dec_hs(_, _, _, _) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+
+encrypted_premaster_secret(Secret, RSAPublicKey) ->
+ try
+ PreMasterSecret = public_key:encrypt_public(Secret, RSAPublicKey,
+ [{rsa_pad,
+ rsa_pkcs1_padding}]),
+ #encrypted_premaster_secret{premaster_secret = PreMasterSecret}
+ catch
+ _:_->
+ 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, []).
+
+certs_to_list(<<?UINT24(CertLen), Cert:CertLen/binary, Rest/binary>>, Acc) ->
+ certs_to_list(Rest, [Cert | Acc]);
+certs_to_list(<<>>, Acc) ->
+ lists:reverse(Acc, []).
+
+certs_from_list(ACList) ->
+ list_to_binary([begin
+ CertLen = byte_size(Cert),
+ <<?UINT24(CertLen), Cert/binary>>
+ end || Cert <- ACList]).
+
+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, _) ->
+ SIDLength = byte_size(SessionID),
+ BinCompMethods = list_to_binary(CompMethods),
+ CmLength = byte_size(BinCompMethods),
+ BinCipherSuites = list_to_binary(CipherSuites),
+ CsLength = byte_size(BinCipherSuites),
+ {?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, _) ->
+ SID_length = byte_size(Session_ID),
+ {?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, _) ->
+ 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),
+ {?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod/binary,
+ ?UINT16(ExpLen), Exp/binary,
+ SignedParams/binary>>
+ };
+enc_hs(#server_key_exchange{params = #server_dh_params{
+ dh_p = P, dh_g = G, dh_y = Y},
+ signed_params = SignedParams}, _Version, _) ->
+ PLen = byte_size(P),
+ GLen = byte_size(G),
+ YLen = byte_size(Y),
+ {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?UINT16(YLen), Y:YLen/binary,
+ SignedParams/binary>>
+ };
+enc_hs(#certificate_request{certificate_types = CertTypes,
+ certificate_authorities = CertAuths},
+ _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, _) ->
+ {?SERVER_HELLO_DONE, <<>>};
+enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version, _) ->
+ {?CLIENT_KEY_EXCHANGE, enc_cke(ExchangeKeys, Version)};
+enc_hs(#certificate_verify{signature = BinSig}, _, _) ->
+ EncSig = enc_bin_sig(BinSig),
+ {?CERTIFICATE_VERIFY, EncSig};
+enc_hs(#finished{verify_data = VerifyData}, _Version, _) ->
+ {?FINISHED, VerifyData}.
+
+enc_cke(#encrypted_premaster_secret{premaster_secret = PKEPMS},{3, 0}) ->
+ PKEPMS;
+enc_cke(#encrypted_premaster_secret{premaster_secret = PKEPMS}, _) ->
+ PKEPMSLen = byte_size(PKEPMS),
+ <<?UINT16(PKEPMSLen), PKEPMS/binary>>;
+enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) ->
+ Len = byte_size(DHPublic),
+ <<?UINT16(Len), DHPublic/binary>>.
+
+enc_bin_sig(BinSig) ->
+ Size = byte_size(BinSig),
+ <<?UINT16(Size), BinSig/binary>>.
+
+init_hashes() ->
+ T = {crypto:md5_init(), crypto:sha_init()},
+ {T, T}.
+
+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, []).
+
+from_3bytes(<<>>, Acc) ->
+ lists:reverse(Acc);
+from_3bytes(<<?UINT24(N), Rest/binary>>, Acc) ->
+ from_3bytes(Rest, [?uint16(N) | Acc]).
+
+from_2bytes(Bin2) ->
+ from_2bytes(Bin2, []).
+
+from_2bytes(<<>>, Acc) ->
+ lists:reverse(Acc);
+from_2bytes(<<?UINT16(N), Rest/binary>>, Acc) ->
+ from_2bytes(Rest, [?uint16(N) | 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(_) ->
+ %%TODO Make list of know CA:s
+ <<>>.
+
+digitally_signed(Hashes, #'RSAPrivateKey'{} = Key) ->
+ public_key:encrypt_private(Hashes, Key,
+ [{rsa_pad, rsa_pkcs1_padding}]);
+digitally_signed(Hashes, #'DSAPrivateKey'{} = Key) ->
+ public_key:sign(Hashes, Key).
+
+
+calc_master_secret({3,0}, PremasterSecret, ClientRandom, ServerRandom) ->
+ ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom);
+
+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,
+ ServerRandom, ClientRandom, HashSize, KML, EKML, IVS) ->
+ ssl_ssl3:setup_keys(Exportable, MasterSecret, ServerRandom,
+ ClientRandom, HashSize, KML, EKML, IVS);
+
+setup_keys({3,1}, _Exportable, 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).
+
+calc_finished({3, 0}, Role, MasterSecret, Hashes) ->
+ ssl_ssl3:finished(Role, MasterSecret, Hashes);
+calc_finished({3, N}, Role, MasterSecret, Hashes)
+ when N == 1; N == 2 ->
+ ssl_tls1:finished(Role, MasterSecret, Hashes).
+
+calc_certificate_verify({3, 0}, MasterSecret, Algorithm, Hashes) ->
+ ssl_ssl3:certificate_verify(Algorithm, MasterSecret, Hashes);
+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 ->
+%% MD5 = crypto:md5_final(Value),
+%% SHA = crypto:sha_final(Value),
+%% <<MD5/binary, SHA/binary>>;
+
+%% server_key_exchange_hash(Algorithm, Value) when Algorithm == dh_dss;
+%% Algorithm == dhe_dss ->
+%% crypto:sha_final(Value).
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
new file mode 100644
index 0000000000..b2bdfa0934
--- /dev/null
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -0,0 +1,200 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Record and constant defenitions for the SSL-handshake protocol
+%% see RFC 4346
+%%----------------------------------------------------------------------
+
+-ifndef(ssl_handshake).
+-define(ssl_handshake, true).
+
+-record(session, {
+ session_id,
+ peer_certificate,
+ compression_method,
+ cipher_suite,
+ master_secret,
+ is_resumable,
+ time_stamp
+ }).
+
+-define(NUM_OF_SESSION_ID_BYTES, 32). % TSL 1.1 & SSL 3
+-define(NUM_OF_PREMASTERSECRET_BYTES, 48).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Handsake protocol - RFC 4346 section 7.4
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% enum {
+%% hello_request(0), client_hello(1), server_hello(2),
+%% certificate(11), server_key_exchange (12),
+%% certificate_request(13), server_hello_done(14),
+%% certificate_verify(15), client_key_exchange(16),
+%% finished(20), (255)
+%% } HandshakeType;
+
+-define(HELLO_REQUEST, 0).
+-define(CLIENT_HELLO, 1).
+-define(CLIENT_HELLO_V2, 3).
+-define(SERVER_HELLO, 2).
+-define(CERTIFICATE, 11).
+-define(SERVER_KEY_EXCHANGE, 12).
+-define(CERTIFICATE_REQUEST, 13).
+-define(SERVER_HELLO_DONE, 14).
+-define(CERTIFICATE_VERIFY, 15).
+-define(CLIENT_KEY_EXCHANGE, 16).
+-define(FINISHED, 20).
+
+-record(random, {
+ gmt_unix_time, % uint32
+ random_bytes % opaque random_bytes[28]
+ }).
+
+%% enum { null(0), (255) } CompressionMethod;
+% -define(NULL, 0). %% Already defined by ssl_internal.hrl
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Hello messages - RFC 4346 section 7.4.2
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-record(client_hello, {
+ client_version,
+ random,
+ session_id, % opaque SessionID<0..32>
+ cipher_suites, % cipher_suites<2..2^16-1>
+ compression_methods % compression_methods<1..2^8-1>
+ }).
+
+-record(server_hello, {
+ server_version,
+ random,
+ session_id, % opaque SessionID<0..32>
+ cipher_suite, % cipher_suites
+ compression_method % compression_method
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Server authentication and key exchange messages - RFC 4346 section 7.4.3
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% opaque ASN.1Cert<2^24-1>;
+
+-record(certificate, {
+ asn1_certificates %% certificate_list<1..2^24-1>
+ }).
+
+%% enum { rsa, diffie_hellman } KeyExchangeAlgorithm;
+
+-define(KEY_EXCHANGE_RSA, 0).
+-define(KEY_EXCHANGE_DIFFIE_HELLMAN, 1).
+
+-record(server_rsa_params, {
+ rsa_modulus, %% opaque RSA_modulus<1..2^16-1>
+ rsa_exponent %% opaque RSA_exponent<1..2^16-1>
+ }).
+
+-record(server_dh_params, {
+ dh_p, %% opaque DH_p<1..2^16-1>
+ dh_g, %% opaque DH_g<1..2^16-1>
+ dh_y %% opaque DH_Ys<1..2^16-1>
+ }).
+
+-record(server_key_exchange, {
+ params, %% #server_rsa_params{} | #server_dh_params{}
+ signed_params %% #signature{}
+ }).
+
+%% enum { anonymous, rsa, dsa } SignatureAlgorithm;
+
+-define(SIGNATURE_ANONYMOUS, 0).
+-define(SIGNATURE_RSA, 1).
+-define(SIGNATURE_DSA, 2).
+
+-record(hello_request, {}).
+-record(server_hello_done, {}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Certificate request - RFC 4346 section 7.4.4
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% enum {
+%% rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
+%% (255)
+%% } ClientCertificateType;
+
+-define(RSA_SIGN, 1).
+-define(DSS_SIGN, 2).
+-define(RSA_FIXED_DH, 3).
+-define(DSS_FIXED_DH, 4).
+
+% opaque DistinguishedName<1..2^16-1>;
+
+-record(certificate_request, {
+ certificate_types, %ClientCertificateType <1..2^8-1>
+ certificate_authorities %DistinguishedName <0..2^16-1>
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Client authentication and key exchange messages - RFC 4346 section 7.4.7
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(client_key_exchange, {
+ exchange_keys %% #encrypted_premaster_secret{} (rsa ) |
+ %% DiffieHellmanClientPublicValue
+ }).
+
+-record(pre_master_secret, {
+ client_version, % ProtocolVersion client_version
+ random % opaque random[46];
+ }).
+
+-record(encrypted_premaster_secret, {
+ premaster_secret
+ }).
+
+%% enum { implicit, explicit } PublicValueEncoding;
+
+-define(IMPLICIT, 0).
+-define(EXPLICIT, 1).
+
+-record(client_diffie_hellman_public, {
+ dh_public
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Certificate verify - RFC 4346 section 7.4.8
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-record(certificate_verify, {
+ signature % binary()
+ }).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Handshake finalization message RFC 4346 section 7.4.9
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-record(finished, {
+ verify_data %opaque verify_data[12]
+ }).
+
+-endif. % -ifdef(ssl_handshake).
+
+
+
diff --git a/lib/ssl/src/ssl_int.hrl b/lib/ssl/src/ssl_int.hrl
new file mode 100644
index 0000000000..3686deffce
--- /dev/null
+++ b/lib/ssl/src/ssl_int.hrl
@@ -0,0 +1,99 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-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%
+%%
+
+%%
+
+%% op codes commands are in capital and reply codes in lower case
+
+-define(CONNECT, 1).
+-define(CONNECT_WAIT, 2).
+-define(CONNECT_REP, 3).
+-define(CONNECT_ERR, 4).
+
+-define(TERMINATE, 5).
+-define(CLOSE, 6).
+
+-define(LISTEN, 7).
+-define(LISTEN_REP, 8).
+-define(LISTEN_ERR, 9).
+
+-define(TRANSPORT_ACCEPT, 10).
+-define(NOACCEPT, 11).
+-define(TRANSPORT_ACCEPT_REP, 12).
+-define(TRANSPORT_ACCEPT_ERR, 13).
+
+-define(FROMNET_CLOSE, 14).
+
+-define(CONNECT_SYNC_ERR, 15).
+-define(LISTEN_SYNC_ERR, 16).
+
+-define(PROXY_PORT, 23).
+-define(PROXY_JOIN, 24).
+-define(PROXY_JOIN_REP, 25).
+-define(PROXY_JOIN_ERR, 26).
+
+-define(SET_SOCK_OPT, 27).
+-define(IOCTL_OK, 28).
+-define(IOCTL_ERR, 29).
+
+-define(GETPEERNAME, 30).
+-define(GETPEERNAME_REP, 31).
+-define(GETPEERNAME_ERR, 32).
+
+-define(GETSOCKNAME, 33).
+-define(GETSOCKNAME_REP, 34).
+-define(GETSOCKNAME_ERR, 35).
+
+-define(GETPEERCERT, 36).
+-define(GETPEERCERT_REP, 37).
+-define(GETPEERCERT_ERR, 38).
+
+-define(GETVERSION, 39).
+-define(GETVERSION_REP, 40).
+
+-define(SET_SEED, 41).
+
+-define(GETCONNINFO, 42).
+-define(GETCONNINFO_REP, 43).
+-define(GETCONNINFO_ERR, 44).
+
+-define(SSL_ACCEPT, 45).
+-define(SSL_ACCEPT_REP, 46).
+-define(SSL_ACCEPT_ERR, 47).
+
+-define(DUMP_CMD, 48).
+-define(DEBUG_CMD, 49).
+-define(DEBUGMSG_CMD, 50).
+
+%% --------------
+
+-define(SSLv2, 1).
+-define(SSLv3, 2).
+-define(TLSv1, 4).
+
+
+%% Set socket options codes 'SET_SOCK_OPT'
+-define(SET_TCP_NODELAY, 1).
+
+-define(DEF_BACKLOG, 128).
+
+-define(DEF_TIMEOUT, 10000).
+
+-record(sslsocket, { fd = nil, pid = nil}).
+
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
new file mode 100644
index 0000000000..23a5c93452
--- /dev/null
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -0,0 +1,91 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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_internal).
+-define(ssl_internal, true).
+
+%% basic binary constructors
+-define(BOOLEAN(X), X:8/unsigned-big-integer).
+-define(BYTE(X), X:8/unsigned-big-integer).
+-define(UINT16(X), X:16/unsigned-big-integer).
+-define(UINT24(X), X:24/unsigned-big-integer).
+-define(UINT32(X), X:32/unsigned-big-integer).
+-define(UINT64(X), X:64/unsigned-big-integer).
+-define(STRING(X), ?UINT32((size(X))), (X)/binary).
+
+-define(byte(X), << ?BYTE(X) >> ).
+-define(uint16(X), << ?UINT16(X) >> ).
+-define(uint24(X), << ?UINT24(X) >> ).
+-define(uint32(X), << ?UINT32(X) >> ).
+-define(uint64(X), << ?UINT64(X) >> ).
+
+-define(CDR_MAGIC, "GIOP").
+-define(CDR_HDR_SIZE, 12).
+
+-define(DEFAULT_TIMEOUT, 5000).
+
+%% Common enumerate values in for SSL-protocols
+-define(NULL, 0).
+-define(TRUE, 0).
+-define(FALSE, 1).
+
+-define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). % TODO: This is temporary
+%-define(DEFAULT_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]).
+
+-record(ssl_options, {
+ versions, % 'tlsv1.1' | tlsv1 | sslv3
+ verify, % verify_none | verify_peer
+ verify_fun, % fun(CertVerifyErrors) -> boolean()
+ fail_if_no_peer_cert, % boolean()
+ verify_client_once, % boolean()
+ depth, % integer()
+ certfile, % file()
+ keyfile, % file()
+ key, %
+ password, %
+ cacertfile, % file()
+ ciphers, %
+ %% Local policy for the server if it want's to reuse the session
+ %% or not. Defaluts to allways returning true.
+ %% fun(SessionId, PeerCert, Compression, CipherSuite) -> boolean()
+ reuse_session,
+ %% If false sessions will never be reused, if true they
+ %% will be reused if possible.
+ reuse_sessions, % boolean()
+ debug %
+ }).
+
+-record(socket_options,
+ {
+ mode = list,
+ packet = 0,
+ packet_size = 0,
+ header = 0,
+ active = true
+ }).
+
+-endif. % -ifdef(ssl_internal).
+
+
+
+
+
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
new file mode 100644
index 0000000000..6b83c2ea46
--- /dev/null
+++ b/lib/ssl/src/ssl_manager.erl
@@ -0,0 +1,340 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Manages ssl sessions and trusted certifacates
+%%----------------------------------------------------------------------
+
+-module(ssl_manager).
+-behaviour(gen_server).
+
+%% Internal application API
+-export([start_link/0, start_link/1,
+ connection_init/2, cache_pem_file/1,
+ lookup_trusted_cert/3, client_session_id/3, server_session_id/3,
+ register_session/2, register_session/3, invalidate_session/2,
+ invalidate_session/3]).
+
+% Spawn export
+-export([init_session_validator/1]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-include("ssl_handshake.hrl").
+-include("ssl_internal.hrl").
+
+-record(state, {
+ session_cache,
+ session_cache_cb,
+ session_lifetime,
+ certificate_db,
+ session_validation_timer
+ }).
+
+-define('24H_in_msec', 8640000).
+-define('24H_in_sec', 8640).
+-define(SESSION_VALIDATION_INTERVAL, 60000).
+-define(CERTIFICATE_CACHE_CLEANUP, 30000).
+
+%%====================================================================
+%% API
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% 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:
+%%--------------------------------------------------------------------
+connection_init(TrustedcertsFile, Role) ->
+ call({connection_init, TrustedcertsFile, Role}).
+
+cache_pem_file(File) ->
+ case ets:lookup(ssl_file_to_ref,File) of
+ [{_,_,Content}] ->
+ {ok, Content};
+ [] ->
+ {ok, Db} = call({cache_pem, File}),
+ [{_,_,Content}] = ets:lookup(Db,File),
+ {ok, Content}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function:
+%% Description:
+%%--------------------------------------------------------------------
+lookup_trusted_cert(SerialNumber, Issuer, Ref) ->
+ ssl_certificate_db:lookup_trusted_cert(Ref, SerialNumber, Issuer).
+
+%%--------------------------------------------------------------------
+%% Function:
+%% Description:
+%%--------------------------------------------------------------------
+client_session_id(Host, Port, SslOpts) ->
+ call({client_session_id, Host, Port, SslOpts}).
+
+%%--------------------------------------------------------------------
+%% Function:
+%% Description:
+%%--------------------------------------------------------------------
+server_session_id(Port, SuggestedSessionId, SslOpts) ->
+ call({server_session_id, Port, SuggestedSessionId, SslOpts}).
+
+%%--------------------------------------------------------------------
+%% Function:
+%% Description:
+%%--------------------------------------------------------------------
+register_session(Host, Port, Session) ->
+ cast({register_session, Host, Port, Session}).
+
+register_session(Port, Session) ->
+ cast({register_session, Port, Session}).
+
+%%--------------------------------------------------------------------
+%% Function:
+%% Description:
+%%--------------------------------------------------------------------
+invalidate_session(Host, Port, Session) ->
+ cast({invalidate_session, Host, Port, Session}).
+
+invalidate_session(Port, Session) ->
+ cast({invalidate_session, Port, Session}).
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% Description: Initiates the server
+%%--------------------------------------------------------------------
+init(Opts) ->
+ process_flag(trap_exit, true),
+ CacheCb = proplists:get_value(session_cache, Opts, ssl_session_cache),
+ SessionLifeTime =
+ proplists:get_value(session_lifetime, Opts, ?'24H_in_sec'),
+ CertDb = ssl_certificate_db:create(),
+ SessionCache = CacheCb:init(),
+ 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_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}
+%% Description: Handling call messages
+%%--------------------------------------------------------------------
+handle_call({{connection_init, "", _Role}, Pid}, _From,
+ #state{session_cache = Cache} = State) ->
+ erlang:monitor(process, Pid),
+ Result = {ok, make_ref(), Cache},
+ {reply, Result, State};
+
+handle_call({{connection_init, TrustedcertsFile, _Role}, Pid}, _From,
+ #state{certificate_db = Db,
+ session_cache = Cache} = State) ->
+ erlang:monitor(process, Pid),
+ Result =
+ case (catch ssl_certificate_db:add_trusted_certs(Pid,
+ TrustedcertsFile,
+ Db)) of
+ {ok, Ref} ->
+ {ok, Ref, Cache};
+ Error ->
+ {error, Error}
+ end,
+ {reply, Result, State};
+
+handle_call({{client_session_id, Host, Port, SslOpts}, _}, _,
+ #state{session_cache = Cache,
+ session_cache_cb = CacheCb} = State) ->
+ Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb),
+ {reply, Id, State};
+
+handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts}, _},
+ _, #state{session_cache_cb = CacheCb,
+ session_cache = Cache,
+ session_lifetime = LifeTime} = State) ->
+ Id = ssl_session:id(Port, SuggestedSessionId, SslOpts,
+ Cache, CacheCb, LifeTime),
+ {reply, Id, State};
+
+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 _:Reason ->
+ {reply, {error, Reason}, State}
+ end;
+
+handle_call(_,_, State) ->
+ {reply, ok, State}.
+%%--------------------------------------------------------------------
+%% Function: handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast({register_session, Host, Port, Session},
+ #state{session_cache = Cache,
+ session_cache_cb = CacheCb} = State) ->
+ TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
+ NewSession = Session#session{time_stamp = TimeStamp},
+ CacheCb:update(Cache, {{Host, Port},
+ NewSession#session.session_id}, NewSession),
+ {noreply, State};
+
+handle_cast({register_session, Port, Session},
+ #state{session_cache = Cache,
+ session_cache_cb = CacheCb} = State) ->
+ TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
+ NewSession = Session#session{time_stamp = TimeStamp},
+ CacheCb:update(Cache, {Port, NewSession#session.session_id}, NewSession),
+ {noreply, State};
+
+handle_cast({invalidate_session, Host, Port,
+ #session{session_id = ID}},
+ #state{session_cache = Cache,
+ session_cache_cb = CacheCb} = State) ->
+ CacheCb:delete(Cache, {{Host, Port}, ID}),
+ {noreply, State};
+
+handle_cast({invalidate_session, Port, #session{session_id = ID}},
+ #state{session_cache = Cache,
+ session_cache_cb = CacheCb} = State) ->
+ CacheCb:delete(Cache, {Port, ID}),
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling all non call/cast messages
+%%--------------------------------------------------------------------
+handle_info(validate_sessions, #state{session_cache_cb = CacheCb,
+ session_cache = Cache,
+ session_lifetime = LifeTime
+ } = State) ->
+ Timer = erlang:send_after(?SESSION_VALIDATION_INTERVAL,
+ self(), validate_sessions),
+ start_session_validator(Cache, CacheCb, LifeTime),
+ {noreply, State#state{session_validation_timer = Timer}};
+
+handle_info({'EXIT', _, _}, State) ->
+ %% Session validator died!! Do we need to take any action?
+ %% maybe error log
+ {noreply, State};
+
+handle_info({'DOWN', _Ref, _Type, _Pid, ecacertfile}, State) ->
+ {noreply, State};
+
+handle_info({'DOWN', _Ref, _Type, Pid, _Reason}, State) ->
+ erlang:send_after(?CERTIFICATE_CACHE_CLEANUP, self(),
+ {remove_trusted_certs, Pid}),
+ {noreply, State};
+handle_info({remove_trusted_certs, Pid},
+ State = #state{certificate_db = Db}) ->
+ ssl_certificate_db:remove_trusted_certs(Pid, Db),
+ {noreply, State};
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% 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.
+%% The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(_Reason, #state{certificate_db = Db,
+ session_cache = SessionCache,
+ session_cache_cb = CacheCb,
+ session_validation_timer = Timer}) ->
+ erlang:cancel_timer(Timer),
+ ssl_certificate_db:remove(Db),
+ CacheCb:terminate(SessionCache),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+call(Msg) ->
+ gen_server:call(?MODULE, {Msg, self()}, infinity).
+
+cast(Msg) ->
+ gen_server:cast(?MODULE, Msg).
+
+validate_session(Host, Port, Session, LifeTime) ->
+ case ssl_session:valid_session(Session, LifeTime) of
+ true ->
+ ok;
+ false ->
+ invalidate_session(Host, Port, Session)
+ end.
+
+validate_session(Port, Session, LifeTime) ->
+ case ssl_session:valid_session(Session, LifeTime) of
+ true ->
+ ok;
+ false ->
+ invalidate_session(Port, Session)
+ end.
+
+start_session_validator(Cache, CacheCb, LifeTime) ->
+ spawn_link(?MODULE, init_session_validator,
+ [[Cache, CacheCb, LifeTime]]).
+
+init_session_validator([Cache, CacheCb, LifeTime]) ->
+ CacheCb:foldl(fun session_validation/2,
+ LifeTime, Cache).
+
+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
new file mode 100644
index 0000000000..0a1bf0f32a
--- /dev/null
+++ b/lib/ssl/src/ssl_pem.erl
@@ -0,0 +1,147 @@
+%%
+%% %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
new file mode 100644
index 0000000000..8f540f74ad
--- /dev/null
+++ b/lib/ssl/src/ssl_pkix.erl
@@ -0,0 +1,307 @@
+%%
+%% %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
new file mode 100644
index 0000000000..a8463369f6
--- /dev/null
+++ b/lib/ssl/src/ssl_pkix.hrl
@@ -0,0 +1,81 @@
+%%
+%% %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_prim.erl b/lib/ssl/src/ssl_prim.erl
new file mode 100644
index 0000000000..e3140a89d1
--- /dev/null
+++ b/lib/ssl/src/ssl_prim.erl
@@ -0,0 +1,173 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-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: Primitive interface to SSL, without broker process (used by
+%% SSL distribution).
+
+-module(ssl_prim).
+
+-export([listen/2, connect/3, accept/1, close/1, send/2, send/3, recv/2, recv/3,
+ getll/1, getstat/2, setopts/2, controlling_process/2, peername/1,
+ sockname/1, getif/1]).
+
+-include("ssl_int.hrl").
+-include("ssl_broker_int.hrl").
+
+%-define(filter(Call), filter((catch Call))).
+-define(filter(Call), filter(Call)).
+
+listen(Port, Opts) ->
+ St = newstate(listener),
+ ?filter(ssl_broker:listen_prim(ssl_server_prim, self(), Port, nonactive(Opts), St)).
+
+connect(Address, Port, Opts) ->
+ St = newstate(connector),
+ ?filter(ssl_broker:connect_prim(ssl_server_prim, inet_tcp, self(), Address,
+ Port, nonactive(Opts), infinity, St)).
+
+accept(#st{} = ListenSt0) ->
+ case transport_accept(ListenSt0) of
+ {ok, ListenSt1} ->
+ ssl_accept(ListenSt0, ListenSt1);
+ Error ->
+ Error
+ end.
+
+transport_accept(#st{opts = ListenOpts, thissock = ListenSocket}) ->
+ NewSt = newstate(acceptor),
+ ListenFd = ListenSocket#sslsocket.fd,
+ ?filter(ssl_broker:transport_accept_prim(ssl_server_prim, ListenFd,
+ ListenOpts, infinity, NewSt)).
+
+ssl_accept(#st{opts = LOpts}, ListenSt1) ->
+ ?filter(ssl_broker:ssl_accept_prim(ssl_server_prim, gen_tcp, self(),
+ LOpts, infinity, ListenSt1)).
+
+close(#st{fd = Fd}) when is_integer(Fd) ->
+ ssl_server:close_prim(ssl_server_prim, Fd),
+ ok;
+close(_) ->
+ ok.
+
+send(St, Data) ->
+ send(St, Data, []).
+
+send(#st{proxysock = Proxysock, status = open}, Data, Opts) ->
+ case inet_tcp:send(Proxysock, Data, Opts) of
+ ok ->
+ ok;
+ {error, _} ->
+ {error, closed}
+ end;
+send(#st{}, _Data, _Opts) ->
+ {error, closed}.
+
+recv(St, Length) ->
+ recv(St, Length, infinity).
+
+recv(#st{proxysock = Proxysock, status = open}, Length, Tmo) ->
+ inet_tcp:recv(Proxysock, Length, Tmo);
+recv(#st{}, _Length, _Tmo) ->
+ {error, closed}.
+
+getll(#st{proxysock = Proxysock, status = open}) ->
+ inet:getll(Proxysock);
+getll(#st{}) ->
+ {error, closed}.
+
+getstat(#st{proxysock = Proxysock, status = open}, Opts) ->
+ inet:getstat(Proxysock, Opts);
+getstat(#st{}, _Opts) ->
+ {error, closed}.
+
+setopts(#st{proxysock = Proxysock, status = open}, Opts) ->
+ case remove_supported(Opts) of
+ [] ->
+ inet:setopts(Proxysock, Opts);
+ _ ->
+ {error, enotsup}
+ end;
+setopts(#st{}, _Opts) ->
+ {error, closed}.
+
+
+controlling_process(#st{proxysock = Proxysock, status = open}, Pid)
+ when is_pid(Pid) ->
+ inet_tcp:controlling_process(Proxysock, Pid);
+controlling_process(#st{}, Pid) when is_pid(Pid) ->
+ {error, closed}.
+
+peername(#st{fd = Fd, status = open}) ->
+ case ssl_server:peername_prim(ssl_server_prim, Fd) of
+ {ok, {Address, Port}} ->
+ {ok, At} = inet_parse:ipv4_address(Address),
+ {ok, {At, Port}};
+ Error ->
+ Error
+ end;
+peername(#st{}) ->
+ {error, closed}.
+
+sockname(#st{fd = Fd, status = open}) ->
+ case ssl_server:sockname_prim(ssl_server_prim, Fd) of
+ {ok, {Address, Port}} ->
+ {ok, At} = inet_parse:ipv4_address(Address),
+ {ok, {At, Port}};
+ Error ->
+ Error
+ end;
+sockname(#st{}) ->
+ {error, closed}.
+
+getif(#st{proxysock = Proxysock, status = open}) ->
+ inet:getif(Proxysock);
+getif(#st{}) ->
+ {error, closed}.
+
+remove_supported([{active, _}|T]) ->
+ remove_supported(T);
+remove_supported([{packet,_}|T]) ->
+ remove_supported(T);
+remove_supported([{deliver,_}|T]) ->
+ remove_supported(T);
+remove_supported([H|T]) ->
+ [H | remove_supported(T)];
+remove_supported([]) ->
+ [].
+
+filter(Result) ->
+ case Result of
+ {ok, _Sock,St} ->
+ {ok, St};
+ {error, Reason, _St} ->
+ {error,Reason}
+ end.
+
+nonactive([{active,_}|T]) ->
+ nonactive(T);
+nonactive([H|T]) ->
+ [H | nonactive(T)];
+nonactive([]) ->
+ [{active, false}].
+
+newstate(Type) ->
+ #st{brokertype = Type, server = whereis(ssl_server_prim),
+ client = undefined, collector = undefined, debug = false}.
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
new file mode 100644
index 0000000000..37a3d1b639
--- /dev/null
+++ b/lib/ssl/src/ssl_record.erl
@@ -0,0 +1,577 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Help functions for handling the SSL-Record protocol
+%%
+%%----------------------------------------------------------------------
+
+-module(ssl_record).
+
+-include("ssl_record.hrl").
+-include("ssl_internal.hrl").
+-include("ssl_alert.hrl").
+-include("ssl_handshake.hrl").
+-include("ssl_debug.hrl").
+
+%% Connection state handling
+-export([init_connection_states/1,
+ current_connection_state/2, pending_connection_state/2,
+ update_security_params/3,
+ set_mac_secret/4,
+ set_master_secret/2,
+ activate_pending_connection_state/2,
+ set_pending_cipher_state/4]).
+
+%% Handling of incoming data
+-export([get_tls_records/2]).
+
+%% Encoding records
+-export([encode_handshake/3, encode_alert_record/3,
+ encode_change_cipher_spec/2, encode_data/3]).
+
+%% Decoding
+-export([decode_cipher_text/2]).
+
+%% Misc.
+-export([protocol_version/1, lowest_protocol_version/2,
+ highest_protocol_version/1, supported_protocol_versions/0,
+ is_acceptable_version/1]).
+
+-export([compressions/0]).
+
+-compile(inline).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: init_connection_states(Role) -> #connection_states{}
+%% Role = client | server
+%% Random = binary()
+%%
+%% Description: Creates a connection_states record with appropriate
+%% values for the initial SSL connection setup.
+%%--------------------------------------------------------------------
+init_connection_states(Role) ->
+ ConnectionEnd = record_protocol_role(Role),
+ Current = initial_connection_state(ConnectionEnd),
+ Pending = empty_connection_state(ConnectionEnd),
+ #connection_states{current_read = Current,
+ pending_read = Pending,
+ current_write = Current,
+ pending_write = Pending
+ }.
+
+%%--------------------------------------------------------------------
+%% Function: current_connection_state(States, Type) -> #connection_state{}
+%% States = #connection_states{}
+%% Type = read | write
+%%
+%% Description: Returns the instance of the connection_state record
+%% that is currently defined as the current conection state.
+%%--------------------------------------------------------------------
+current_connection_state(#connection_states{current_read = Current},
+ read) ->
+ Current;
+current_connection_state(#connection_states{current_write = Current},
+ write) ->
+ Current.
+
+%%--------------------------------------------------------------------
+%% Function: pending_connection_state(States, Type) -> #connection_state{}
+%% States = #connection_states{}
+%% Type = read | write
+%%
+%% Description: Returns the instance of the connection_state record
+%% that is currently defined as the pending conection state.
+%%--------------------------------------------------------------------
+pending_connection_state(#connection_states{pending_read = Pending},
+ read) ->
+ Pending;
+pending_connection_state(#connection_states{pending_write = Pending},
+ write) ->
+ Pending.
+
+%%--------------------------------------------------------------------
+%% Function: update_security_params(Params, States) ->
+%% #connection_states{}
+%% Params = #security_parameters{}
+%% States = #connection_states{}
+%%
+%% Description: Creates a new instance of the connection_states record
+%% where the pending states gets its security parameters
+%% updated to <Params>.
+%%--------------------------------------------------------------------
+update_security_params(ReadParams, WriteParams, States =
+ #connection_states{pending_read = Read,
+ pending_write = Write}) ->
+ States#connection_states{pending_read =
+ Read#connection_state{security_parameters =
+ ReadParams},
+ pending_write =
+ Write#connection_state{security_parameters =
+ WriteParams}
+ }.
+%%--------------------------------------------------------------------
+%% Function: set_mac_secret(ClientWriteMacSecret,
+%% ServerWriteMacSecret, Role, States) ->
+%% #connection_states{}
+%% MacSecret = binary()
+%% States = #connection_states{}
+%% Role = server | client
+%%
+%% update the mac_secret field in pending connection states
+%%--------------------------------------------------------------------
+set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, client, States) ->
+ set_mac_secret(ServerWriteMacSecret, ClientWriteMacSecret, States);
+set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, server, States) ->
+ set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, States).
+
+set_mac_secret(ReadMacSecret, WriteMacSecret,
+ States = #connection_states{pending_read = Read,
+ pending_write = Write}) ->
+ States#connection_states{
+ pending_read = Read#connection_state{mac_secret = ReadMacSecret},
+ pending_write = Write#connection_state{mac_secret = WriteMacSecret}
+ }.
+
+
+%%--------------------------------------------------------------------
+%% Function: set_master_secret(MasterSecret, States) ->
+%% #connection_states{}
+%% MacSecret =
+%% States = #connection_states{}
+%%
+%% Set master_secret in pending connection states
+%%--------------------------------------------------------------------
+set_master_secret(MasterSecret,
+ States = #connection_states{pending_read = Read,
+ pending_write = Write}) ->
+ ReadSecPar = Read#connection_state.security_parameters,
+ Read1 = Read#connection_state{
+ security_parameters = ReadSecPar#security_parameters{
+ master_secret = MasterSecret}},
+ WriteSecPar = Write#connection_state.security_parameters,
+ Write1 = Write#connection_state{
+ security_parameters = WriteSecPar#security_parameters{
+ master_secret = MasterSecret}},
+ States#connection_states{pending_read = Read1, pending_write = Write1}.
+
+
+%%--------------------------------------------------------------------
+%% Function: activate_pending_connection_state(States, Type) ->
+%% #connection_states{}
+%% States = #connection_states{}
+%% Type = read | write
+%%
+%% Description: Creates a new instance of the connection_states record
+%% where the pending state of <Type> has been activated.
+%%--------------------------------------------------------------------
+activate_pending_connection_state(States =
+ #connection_states{pending_read = Pending},
+ read) ->
+ NewCurrent = Pending#connection_state{sequence_number = 0},
+ SecParams = Pending#connection_state.security_parameters,
+ ConnectionEnd = SecParams#security_parameters.connection_end,
+ NewPending = empty_connection_state(ConnectionEnd),
+ States#connection_states{current_read = NewCurrent,
+ pending_read = NewPending
+ };
+
+activate_pending_connection_state(States =
+ #connection_states{pending_write = Pending},
+ write) ->
+ NewCurrent = Pending#connection_state{sequence_number = 0},
+ SecParams = Pending#connection_state.security_parameters,
+ ConnectionEnd = SecParams#security_parameters.connection_end,
+ NewPending = empty_connection_state(ConnectionEnd),
+ 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{}
+%%
+%% Description: Set the cipher state in the specified pending connection state.
+%%--------------------------------------------------------------------
+set_pending_cipher_state(#connection_states{pending_read = Read,
+ pending_write = Write} = States,
+ ClientState, ServerState, server) ->
+ States#connection_states{
+ pending_read = Read#connection_state{cipher_state = ClientState},
+ pending_write = Write#connection_state{cipher_state = ServerState}};
+
+set_pending_cipher_state(#connection_states{pending_read = Read,
+ pending_write = Write} = States,
+ ClientState, ServerState, client) ->
+ States#connection_states{
+ pending_read = Read#connection_state{cipher_state = ServerState},
+ 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
+%% data
+%%--------------------------------------------------------------------
+get_tls_records(Data, <<>>) ->
+ get_tls_records_aux(Data, []);
+get_tls_records(Data, Buffer) ->
+ get_tls_records_aux(list_to_binary([Buffer, Data]), []).
+
+get_tls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Length), Data:Length/binary, Rest/binary>>,
+ Acc) ->
+ get_tls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA,
+ version = {MajVer, MinVer},
+ fragment = Data} | Acc]);
+get_tls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Length),
+ Data:Length/binary, Rest/binary>>, Acc) ->
+ get_tls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
+ version = {MajVer, MinVer},
+ fragment = Data} | Acc]);
+get_tls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Length), Data:Length/binary,
+ Rest/binary>>, Acc) ->
+ get_tls_records_aux(Rest, [#ssl_tls{type = ?ALERT,
+ version = {MajVer, MinVer},
+ fragment = Data} | Acc]);
+get_tls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer),
+ ?UINT16(Length), Data:Length/binary, Rest/binary>>,
+ Acc) ->
+ get_tls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC,
+ version = {MajVer, MinVer},
+ fragment = Data} | Acc]);
+%% Matches a ssl v2 client hello message.
+%% The server must be able to receive such messages, from clients that
+%% are willing to use ssl v3 or higher, but have ssl v2 compatibility.
+get_tls_records_aux(<<1:1, Length0:15, Data0:Length0/binary, Rest/binary>>,
+ Acc) ->
+ case Data0 of
+ <<?BYTE(?CLIENT_HELLO), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>> ->
+ Length = Length0-1,
+ <<?BYTE(_), Data1:Length/binary>> = Data0,
+ Data = <<?BYTE(?CLIENT_HELLO), ?UINT24(Length), Data1/binary>>,
+ get_tls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE,
+ version = {MajVer, MinVer},
+ fragment = Data} | Acc]);
+ _ ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
+
+ end;
+
+get_tls_records_aux(<<0:1, _CT:7, ?BYTE(_MajVer), ?BYTE(_MinVer),
+ ?UINT16(Length), _/binary>>,
+ _Acc) when Length > ?MAX_CIPHER_TEXT_LENGTH->
+ ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
+
+get_tls_records_aux(<<1:1, Length0:15, _/binary>>,_Acc)
+ when Length0 > ?MAX_CIPHER_TEXT_LENGTH->
+ ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW);
+
+get_tls_records_aux(Data, Acc) ->
+ {lists:reverse(Acc), Data}.
+
+%%--------------------------------------------------------------------
+%% Function: protocol_version(Version) -> #protocol_version{}
+%% Version = atom()
+%%
+%% Description: Creates a protocol version record from a version atom
+%% or vice versa.
+%%--------------------------------------------------------------------
+protocol_version('tlsv1.1') ->
+ {3, 2};
+protocol_version(tlsv1) ->
+ {3, 1};
+protocol_version(sslv3) ->
+ {3, 0};
+protocol_version(sslv2) ->
+ {2, 0};
+protocol_version({3, 2}) ->
+ 'tlsv1.1';
+protocol_version({3, 1}) ->
+ tlsv1;
+protocol_version({3, 0}) ->
+ sslv3;
+protocol_version({2, 0}) ->
+ sslv2.
+%%--------------------------------------------------------------------
+%% Function: protocol_version(Version1, Version2) -> #protocol_version{}
+%% Version1 = Version2 = #protocol_version{}
+%%
+%% Description: Lowes protocol version of two given versions
+%%--------------------------------------------------------------------
+lowest_protocol_version(Version = {M, N}, {M, O}) when N < O ->
+ Version;
+lowest_protocol_version({M, _},
+ Version = {M, _}) ->
+ Version;
+lowest_protocol_version(Version = {M,_},
+ {N, _}) when M < N ->
+ Version;
+lowest_protocol_version(_,Version) ->
+ Version.
+%%--------------------------------------------------------------------
+%% Function: protocol_version(Versions) -> #protocol_version{}
+%% Versions = [#protocol_version{}]
+%%
+%% Description: Highest protocol version present in a list
+%%--------------------------------------------------------------------
+highest_protocol_version([]) ->
+ highest_protocol_version();
+highest_protocol_version(Versions) ->
+ [Ver | Vers] = Versions,
+ highest_protocol_version(Ver, Vers).
+
+highest_protocol_version(Version, []) ->
+ Version;
+highest_protocol_version(Version = {N, M}, [{N, O} | Rest]) when M > O ->
+ highest_protocol_version(Version, Rest);
+highest_protocol_version({M, _}, [Version = {M, _} | Rest]) ->
+ highest_protocol_version(Version, Rest);
+highest_protocol_version(Version = {M,_}, [{N,_} | Rest]) when M > N ->
+ highest_protocol_version(Version, Rest);
+highest_protocol_version(_, [Version | Rest]) ->
+ highest_protocol_version(Version, Rest).
+
+%%--------------------------------------------------------------------
+%% Function: supported_protocol_versions() -> Versions
+%% Versions = [#protocol_version{}]
+%%
+%% Description: Protocol versions supported
+%%--------------------------------------------------------------------
+supported_protocol_versions() ->
+ Fun = fun(Version) ->
+ protocol_version(Version)
+ end,
+ case application:get_env(ssl, protocol_version) of
+ undefined ->
+ lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS);
+ {ok, []} ->
+ lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS);
+ {ok, Vsns} when is_list(Vsns) ->
+ lists:map(Fun, Vsns);
+ {ok, Vsn} ->
+ [Fun(Vsn)]
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: is_acceptable_version(Version) -> true | false
+%% Version = #protocol_version{}
+%%
+%% Description: ssl version 2 is not acceptable security risks are too big.
+%%--------------------------------------------------------------------
+is_acceptable_version({N,_})
+ when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION ->
+ true;
+is_acceptable_version(_) ->
+ false.
+
+%%--------------------------------------------------------------------
+%% Function: compressions() -> binary()
+%%
+%% Description: return a list of compressions supported (currently none)
+%%--------------------------------------------------------------------
+compressions() ->
+ [?byte(?NULL)].
+
+%%--------------------------------------------------------------------
+%% Function: decode_cipher_text(CipherText, ConnectionStates0) ->
+%% {Plain, ConnectionStates}
+%%
+%% Description: Decode cipher text
+%%--------------------------------------------------------------------
+decode_cipher_text(CipherText, ConnnectionStates0) ->
+ ReadState0 = ConnnectionStates0#connection_states.current_read,
+ #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}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+highest_protocol_version() ->
+ highest_protocol_version(supported_protocol_versions()).
+
+initial_connection_state(ConnectionEnd) ->
+ #connection_state{security_parameters =
+ initial_security_params(ConnectionEnd),
+ sequence_number = 0
+ }.
+
+initial_security_params(ConnectionEnd) ->
+ #security_parameters{connection_end = ConnectionEnd,
+ bulk_cipher_algorithm = ?NULL,
+ mac_algorithm = ?NULL,
+ compression_algorithm = ?NULL,
+ cipher_type = ?NULL
+ }.
+
+empty_connection_state(ConnectionEnd) ->
+ SecParams = empty_security_params(ConnectionEnd),
+ #connection_state{security_parameters = SecParams}.
+
+empty_security_params(ConnectionEnd = ?CLIENT) ->
+ #security_parameters{connection_end = ConnectionEnd,
+ client_random = random()};
+empty_security_params(ConnectionEnd = ?SERVER) ->
+ #security_parameters{connection_end = ConnectionEnd,
+ server_random = random()}.
+random() ->
+ Secs_since_1970 = calendar:datetime_to_gregorian_seconds(
+ calendar:universal_time()) - 62167219200,
+ Random_28_bytes = crypto:rand_bytes(28),
+ <<?UINT32(Secs_since_1970), Random_28_bytes/binary>>.
+
+record_protocol_role(client) ->
+ ?CLIENT;
+record_protocol_role(server) ->
+ ?SERVER.
+
+split_bin(Bin, ChunkSize) ->
+ split_bin(Bin, ChunkSize, []).
+
+split_bin(<<>>, _, Acc) ->
+ lists:reverse(Acc);
+split_bin(Bin, ChunkSize, Acc) ->
+ case Bin of
+ <<Chunk:ChunkSize/binary, Rest/binary>> ->
+ split_bin(Rest, ChunkSize, [Chunk | Acc]);
+ _ ->
+ lists:reverse(Acc, [Bin])
+ end.
+
+encode_data(Frag, Version, ConnectionStates)
+ when byte_size(Frag) < (?MAX_PLAIN_TEXT_LENGTH - 2048) ->
+ encode_plain_text(?APPLICATION_DATA,Version,Frag,ConnectionStates);
+encode_data(Frag, Version, ConnectionStates) ->
+ Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH - 2048),
+ {CS1, Acc} =
+ lists:foldl(fun(B, {CS0, Acc}) ->
+ {ET, CS1} =
+ encode_plain_text(?APPLICATION_DATA,
+ Version, B, CS0),
+ {CS1, [ET | Acc]}
+ end, {ConnectionStates, []}, Data),
+ {lists:reverse(Acc), CS1}.
+
+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) ->
+ #connection_states{current_write=#connection_state{
+ compression_state=CompS0,
+ security_parameters=
+ #security_parameters{compression_algorithm=CompAlg}
+ }=CS0} = ConnectionStates,
+ {Comp, CompS1} = compress(CompAlg, Data, CompS0),
+ CS1 = CS0#connection_state{compression_state = CompS1},
+ {CipherText, CS2} = cipher(Type, Version, Comp, CS1),
+ CTBin = encode_tls_cipher_text(Type, Version, CipherText),
+ {CTBin, ConnectionStates#connection_states{current_write = CS2}}.
+
+encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) ->
+ Length = erlang:iolist_size(Fragment),
+ [<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Fragment].
+
+cipher(Type, Version, Fragment, CS0) ->
+ Length = erlang:iolist_size(Fragment),
+ {Hash, 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),
+ CS2 = CS1#connection_state{cipher_state=CipherS1},
+ {Ciphered, CS2}.
+
+decipher(TLS=#ssl_tls{type = ?CHANGE_CIPHER_SPEC}, CS) ->
+ %% These are never encrypted
+ {TLS, CS};
+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?
+ 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}.
+
+uncompress(?NULL, Data = #ssl_tls{type = _Type,
+ version = _Version,
+ fragment = _Fragment}, CS) ->
+ {Data, CS}.
+
+compress(?NULL, Data, CS) ->
+ {Data, CS}.
+
+hash_and_bump_seqno(#connection_state{sequence_number = SeqNo,
+ mac_secret = MacSecret,
+ security_parameters =
+ SecPars} = CS0,
+ Type, Version, Length, Fragment) ->
+ Hash = mac_hash(Version,
+ SecPars#security_parameters.mac_algorithm,
+ MacSecret, SeqNo, Type,
+ Length, Fragment),
+ {Hash, CS0#connection_state{sequence_number = SeqNo+1}}.
+
+check_hash(_, _) ->
+ ok. %% TODO check this
+
+mac_hash(?NULL, {_,_}, _MacSecret, _SeqNo, _Type,
+ _Length, _Fragment) ->
+ <<>>;
+mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
+ ssl_ssl3:mac_hash(MacAlg, MacSecret, SeqNo, Type, Length, Fragment);
+mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment)
+ when N =:= 1; N =:= 2 ->
+ ssl_tls1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version,
+ Length, Fragment).
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
new file mode 100644
index 0000000000..7370e0f0b3
--- /dev/null
+++ b/lib/ssl/src/ssl_record.hrl
@@ -0,0 +1,170 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Record and constant defenitions for the SSL-record protocol
+%% see RFC 2246
+%%----------------------------------------------------------------------
+
+-ifndef(ssl_record).
+-define(ssl_record, true).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Connection states - RFC 4346 section 6.1
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(connection_states, {
+ current_read,
+ pending_read,
+ current_write,
+ pending_write
+ }).
+
+-record(security_parameters, {
+ cipher_suite,
+ connection_end,
+ bulk_cipher_algorithm,
+ cipher_type,
+ iv_size,
+ key_size, % unit 8
+ key_material_length, % unit 8
+ expanded_key_material_length, % unit 8
+ mac_algorithm, % unit 8
+ hash_size, % unit 8
+ compression_algorithm, % unit 8
+ master_secret, % opaque 48
+ client_random, % opaque 32
+ server_random, % opaque 32
+ exportable % boolean
+ }).
+
+-record(connection_state, {
+ security_parameters,
+ compression_state,
+ cipher_state,
+ mac_secret,
+ sequence_number
+ }).
+
+%% ConnectionEnd
+-define(SERVER, 0).
+-define(CLIENT, 1).
+
+%% BulkCipherAlgorithm
+%-define(NULL, 0). %% Already defined by ssl_internal.hrl
+-define(RC4, 1).
+-define(RC2, 2).
+-define(DES, 3).
+-define('3DES', 4).
+-define(DES40, 5).
+-define(IDEA, 6).
+-define(AES, 7).
+
+%% CipherType
+-define(STREAM, 0).
+-define(BLOCK, 1).
+
+%% IsExportable
+%-define(TRUE, 0). %% Already defined by ssl_internal.hrl
+%-define(FALSE, 1). %% Already defined by ssl_internal.hrl
+
+%% MACAlgorithm
+%-define(NULL, 0). %% Already defined by ssl_internal.hrl
+-define(MD5, 1).
+-define(SHA, 2).
+
+%% CompressionMethod
+% -define(NULL, 0). %% Already defined by ssl_internal.hrl
+
+
+-record(compression_state, {
+ method,
+ state
+ }).
+
+%% See also cipher.hrl for #cipher_state{}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Record layer - RFC 2246 section 6.2
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%enum {
+%% change_cipher_spec(20), alert(21), handshake(22),
+%% application_data(23), (255)
+%% } ContentType;
+
+-define(CHANGE_CIPHER_SPEC, 20).
+-define(ALERT, 21).
+-define(HANDSHAKE, 22).
+-define(APPLICATION_DATA, 23).
+-define(MAX_PLAIN_TEXT_LENGTH, 16384).
+-define(MAX_COMPRESSED_LENGTH, (?MAX_PLAIN_TEXT_LENGTH+1024)).
+-define(MAX_CIPHER_TEXT_LENGTH, (?MAX_PLAIN_TEXT_LENGTH+2048)).
+
+%% -record(protocol_version, {
+%% major, % unit 8
+%% minor % unit 8
+%% }).
+
+-define(LOWEST_MAJOR_SUPPORTED_VERSION, 3).
+
+-record(ssl_tls, { %% From inet driver
+ port,
+ type,
+ version,
+ fragment
+ }).
+
+%% -record(tls_plain_text, {
+%% type,
+%% version, % #protocol_version{}
+%% length, % unit 16
+%% fragment % opaque
+%% }).
+
+%% -record(tls_compressed, {
+%% type,
+%% version,
+%% length, % unit 16
+%% fragment % opaque
+%% }).
+
+%% -record(tls_cipher_text, {
+%% type,
+%% version,
+%% length,
+%% cipher,
+%% fragment
+%% }).
+
+-record(generic_stream_cipher, {
+ content, % opaque content[TLSCompressed.length];
+ mac % opaque MAC[CipherSpec.hash_size];
+ }).
+
+-record(generic_block_cipher, {
+ iv, % opaque IV[CipherSpec.block_length];
+ content, % opaque content[TLSCompressed.length];
+ mac, % opaque MAC[CipherSpec.hash_size];
+ padding, % unit 8 padding[GenericBlockCipher.padding_length];
+ padding_length % uint8 padding_length;
+ }).
+
+-endif. % -ifdef(ssl_record).
diff --git a/lib/ssl/src/ssl_server.erl b/lib/ssl/src/ssl_server.erl
new file mode 100644
index 0000000000..b66e20a397
--- /dev/null
+++ b/lib/ssl/src/ssl_server.erl
@@ -0,0 +1,1378 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-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 : SSL server
+
+%%
+%% TODO
+%%
+%% XXX The ip option in listen is not general enough. It is assumed
+%% to be a tuple, which is not always the case.
+
+-module(ssl_server).
+-behaviour(gen_server).
+
+%% External exports
+-export([start_link/0]).
+
+-export([transport_accept/2, transport_accept/3, ssl_accept/2, ssl_accept/3,
+ ciphers/0, connect/5, connect/6,
+ connection_info/1, close/1, listen/3, listen/4, peercert/1,
+ peername/1, proxy_join/2, seed/1, setnodelay/2, sockname/1,
+ version/0]).
+
+-export([start_link_prim/0]).
+-export([ssl_accept_prim/4, transport_accept_prim/4,
+ connect_prim/7, close_prim/2,
+ listen_prim/5, proxy_join_prim/3, peername_prim/2, setnodelay_prim/3,
+ sockname_prim/2]).
+
+-export([dump/0, dump/1]).
+-export([enable_debug/0, disable_debug/0, set_debug/1]).
+-export([enable_debugmsg/0, disable_debugmsg/0, set_debugmsg/1]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ code_change/3, terminate/2]).
+
+-include("ssl_int.hrl").
+
+-record(st, {
+ port = [], % port() of port program
+ progpid = [], % OS pid of port program
+ debug = false, % debug printout flag
+ cons = [], % All brokers except pending accepts
+ paccepts = [], % Pending accept brokers
+ proxylsport = [], % proxy listen socket port
+ intref = 0, % internal reference counter
+ compvsn = "", % ssl compile library version
+ libvsn = "", % ssl library version
+ ciphers = [] % available ciphers
+ }).
+
+
+%% In all functions below IP is a four tuple, e.g. {192, 236, 52, 7}.
+%% Port, Fd and ListenFd are integers; Flags is a string of characters.
+%%
+%% The prefixes F and L mean foreign and local, respectively.
+%% Example: FIP (IP address for foreign end).
+
+%%
+%% start_link() -> {ok, Pid} | {error, Reason}
+%%
+start_link() ->
+ gen_server:start_link({local, ssl_server}, ssl_server, [], []).
+
+start_link_prim() ->
+ gen_server:start_link({local, ssl_server_prim}, ssl_server, [], []).
+
+%%
+%% transport_accept(ListenFd, Flags) -> {ok, Fd, ProxyLLPort} |
+%% {error, Reason}
+%%
+transport_accept(ListenFd, Flags) ->
+ transport_accept(ListenFd, Flags, infinity).
+transport_accept(ListenFd, Flags, Timeout) ->
+ transport_accept_prim(ssl_server,ListenFd, Flags, Timeout).
+
+transport_accept_prim(ServerName, ListenFd, Flags, Timeout) ->
+ Req = {transport_accept, self(), ListenFd, Flags},
+ gen_server:call(ServerName, Req, Timeout).
+
+%%
+%% ssl_accept(ListenFd, Flags) -> {ok, Fd, ProxyLLPort} |
+%% {error, Reason}
+%%
+ssl_accept(ListenFd, Flags) ->
+ ssl_accept(ListenFd, Flags, infinity).
+ssl_accept(ListenFd, Flags, Timeout) ->
+ ssl_accept_prim(ssl_server, ListenFd, Flags, Timeout).
+
+ssl_accept_prim(ServerName, Fd, Flags, Timeout) ->
+ Req = {ssl_accept, Fd, Flags},
+ gen_server:call(ServerName, Req, Timeout).
+
+%%
+%% ciphers() -> {ok, Ciphers}
+%%
+ciphers() ->
+ gen_server:call(ssl_server, ciphers, infinity).
+
+%%
+%% close(Fd) -> ok
+%%
+close(Fd) ->
+ close_prim(ssl_server, Fd).
+close_prim(ServerName, Fd) ->
+ gen_server:call(ServerName, {close, self(), Fd}, infinity),
+ ok.
+
+%%
+%% connect(LIP, LPort, FIP, FPort, Flags) -> {ok, Fd, ProxyLFPort} |
+%% {error, Reason}
+%%
+connect(LIP, LPort, FIP, FPort, Flags) ->
+ connect(LIP, LPort, FIP, FPort, Flags, infinity).
+connect(LIP, LPort, FIP, FPort, Flags, Timeout) ->
+ connect_prim(ssl_server, LIP, LPort, FIP, FPort, Flags, Timeout).
+
+connect_prim(ServerName, LIP, LPort, FIP, FPort, Flags, Timeout) ->
+ Req = {connect, self(), LIP, LPort, FIP, FPort, Flags},
+ gen_server:call(ServerName, Req, Timeout).
+
+%%
+%% connection_info(Fd) -> {ok, {Protocol, Cipher}} | {error, Reason}
+%%
+connection_info(Fd) ->
+ Req = {connection_info, self(), Fd},
+ gen_server:call(ssl_server, Req, infinity).
+
+%%
+%% listen(IP, LPort, Flags),
+%% listen(IP, LPort, Flags, BackLog) -> {ok, ListenFd, LPort0} |
+%% {error, Reason}
+%%
+listen(IP, LPort, Flags) ->
+ listen(IP, LPort, Flags, ?DEF_BACKLOG).
+listen(IP, LPort, Flags, BackLog) ->
+ listen_prim(ssl_server, IP, LPort, Flags, BackLog).
+listen_prim(ServerName, IP, LPort, Flags, BackLog) ->
+ Req = {listen, self(), IP, LPort, Flags, BackLog},
+ gen_server:call(ServerName, Req, infinity).
+
+%%
+%% peercert(Fd) -> {ok, Cert} | {error, Reason}
+%%
+peercert(Fd) ->
+ Req = {peercert, self(), Fd},
+ gen_server:call(ssl_server, Req, infinity).
+
+%%
+%% peername(Fd) -> {ok, {Address, Port}} | {error, Reason}
+%%
+peername(Fd) ->
+ peername_prim(ssl_server, Fd).
+peername_prim(ServerName, Fd) ->
+ Req = {peername, self(), Fd},
+ gen_server:call(ServerName, Req, infinity).
+
+%%
+%% proxy_join(Fd, LPort) -> ok | {error, Reason}
+%%
+proxy_join(Fd, LPort) ->
+ proxy_join_prim(ssl_server, Fd, LPort).
+proxy_join_prim(ServerName, Fd, LPort) ->
+ Req = {proxy_join, self(), Fd, LPort},
+ gen_server:call(ServerName, Req, infinity).
+
+%%
+%% seed(Data)
+%%
+seed(Data) ->
+ Req = {seed, Data},
+ gen_server:call(ssl_server, Req, infinity).
+
+%%
+%% set_nodelay(Fd, Boolean)
+%%
+setnodelay(Fd, Boolean) ->
+ setnodelay_prim(ssl_server, Fd, Boolean).
+setnodelay_prim(ServerName, Fd, Boolean) ->
+ Req = {setnodelay, self(), Fd, Boolean},
+ gen_server:call(ServerName, Req, infinity).
+
+%%
+%% sockname(Fd) -> {ok, {Address, Port}} | {error, Reason}
+%%
+sockname(Fd) ->
+ sockname_prim(ssl_server, Fd).
+sockname_prim(ServerName, Fd) ->
+ Req = {sockname, self(), Fd},
+ gen_server:call(ServerName, Req, infinity).
+
+%%
+%% version() -> {ok, {CompVsn, LibVsn}}
+%%
+version() ->
+ gen_server:call(ssl_server, version, infinity).
+
+
+enable_debug() ->
+ set_debug(true).
+
+disable_debug() ->
+ set_debug(false).
+
+set_debug(Bool) ->
+ set_debug(Bool, infinity).
+
+set_debug(Bool, Timeout) when is_boolean(Bool) ->
+ Req = {set_debug, Bool, self()},
+ gen_server:call(ssl_server, Req, Timeout).
+
+enable_debugmsg() ->
+ set_debugmsg(true).
+
+disable_debugmsg() ->
+ set_debugmsg(false).
+
+set_debugmsg(Bool) ->
+ set_debugmsg(Bool, infinity).
+
+set_debugmsg(Bool, Timeout) when is_boolean(Bool) ->
+ Req = {set_debugmsg, Bool, self()},
+ gen_server:call(ssl_server, Req, Timeout).
+
+dump() ->
+ dump(infinity).
+
+dump(Timeout) ->
+ Req = {dump, self()},
+ gen_server:call(ssl_server, Req, Timeout).
+
+%%
+%% init
+%%
+init([]) ->
+ Debug = case application:get_env(ssl, edebug) of
+ {ok, true} ->
+ true;
+ _ ->
+ case application:get_env(ssl, debug) of
+ {ok, true} ->
+ true;
+ _ ->
+ os:getenv("ERL_SSL_DEBUG") =/= false
+ end
+ end,
+ ProgDir =
+ case init:get_argument(ssl_portprogram_dir) of
+ {ok, [[D]]} ->
+ D;
+ _ ->
+ find_priv_bin()
+ end,
+ {Program, Flags} = mk_cmd_line("ssl_esock"),
+ Cmd = filename:join(ProgDir, Program) ++ " " ++ Flags,
+ debug1(Debug, " start, Cmd = ~s~n", [Cmd]),
+ case (catch open_port({spawn, Cmd}, [binary, {packet, 4}])) of
+ Port when is_port(Port) ->
+ process_flag(trap_exit, true),
+ receive
+ {Port, {data, Bin}} ->
+ {ProxyLLPort, ProgPid, CompVsn, LibVsn, Ciphers} =
+ decode_msg(Bin, [int16, int32, string, string,
+ string]),
+ debug1(Debug, "port program pid = ~w~n",
+ [ProgPid]),
+ {ok, #st{port = Port,
+ proxylsport = ProxyLLPort,
+ progpid = ProgPid,
+ debug = Debug,
+ compvsn = CompVsn,
+ libvsn = LibVsn,
+ ciphers = Ciphers}};
+ {'EXIT', Port, Reason} ->
+ {stop, Reason}
+ end;
+ {'EXIT', Reason} ->
+ {stop, Reason}
+ end.
+
+%%
+%% transport_accept
+%%
+handle_call({transport_accept, Broker, ListenFd, Flags}, From, St) ->
+ debug(St, "transport_accept: broker = ~w, listenfd = ~w~n",
+ [Broker, ListenFd]),
+ case get_by_fd(ListenFd, St#st.cons) of
+ {ok, {ListenFd, _, _}} ->
+ send_cmd(St#st.port, ?TRANSPORT_ACCEPT, [int32(ListenFd), Flags, 0]),
+ PAccepts = add({ListenFd, Broker, From}, St#st.paccepts),
+ %%
+ %% We reply when we get TRANSPORT_ACCEPT_REP or ASYNC_ACCEPT_ERR
+ %%
+ {noreply, St#st{paccepts = PAccepts}};
+ _Other ->
+ {reply, {error, ebadf}, St}
+ end;
+
+%%
+%% ssl_accept
+%%
+handle_call({ssl_accept, Fd, Flags}, From, St) ->
+ case replace_from_by_fd(Fd, St#st.cons, From) of
+ {ok, _, Cons} = _Rep ->
+ send_cmd(St#st.port, ?SSL_ACCEPT, [int32(Fd), Flags, 0]),
+ %% We reply when we get SSL_ACCEPT_REP or ASYNC_ACCEPT_ERR
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ {reply, {error, ebadf}, St}
+ end;
+
+%%
+%% version
+%%
+handle_call(ciphers, From, St) ->
+ debug(St, "ciphers: from = ~w~n", [From]),
+ {reply, {ok, St#st.ciphers}, St};
+
+%%
+%% connect
+%%
+handle_call({connect, Broker, LIP, LPort, FIP, FPort, Flags}, From, St) ->
+ debug(St, "connect: broker = ~w, ip = ~w, "
+ "sport = ~w~n", [Broker, FIP, FPort]),
+ Port = St#st.port,
+ LIPStr = ip_to_string(LIP),
+ FIPStr = ip_to_string(FIP),
+ IntRef = new_intref(St),
+ send_cmd(Port, ?CONNECT, [int32(IntRef),
+ int16(LPort), LIPStr, 0,
+ int16(FPort), FIPStr, 0,
+ Flags, 0]),
+ Cons = add({{intref, IntRef}, Broker, From}, St#st.cons),
+ %% We reply when we have got CONNECT_SYNC_ERR, or CONNECT_WAIT
+ %% and CONNECT_REP, or CONNECT_ERR.
+ {noreply, St#st{cons = Cons, intref = IntRef}};
+
+%%
+%% connection_info
+%%
+handle_call({connection_info, Broker, Fd}, From, St) ->
+ debug(St, "connection_info: broker = ~w, fd = ~w~n",
+ [Broker, Fd]),
+ case replace_from_by_fd(Fd, St#st.cons, From) of
+ {ok, _, Cons} ->
+ send_cmd(St#st.port, ?GETCONNINFO, [int32(Fd)]),
+ %% We reply when we get GETCONNINFO_REP or GETCONNINFO_ERR.
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ {reply, {error, ebadf}, St}
+ end;
+
+%%
+%% close
+%%
+handle_call({close, Broker, Fd}, _From, St) ->
+ debug(St, "close: broker = ~w, fd = ~w~n",
+ [Broker, Fd]),
+ #st{port = Port, cons = Cons0, paccepts = PAccepts0} = St,
+ case delete_by_fd(Fd, Cons0) of
+ %% Must match Broker pid; fd may be reused already.
+ {ok, {Fd, Broker, _}, Cons} ->
+ send_cmd(Port, ?CLOSE, int32(Fd)),
+ %% If Fd is a listen socket fd, there might be pending
+ %% accepts for that fd.
+ case delete_all_by_fd(Fd, PAccepts0) of
+ {ok, DelAccepts, RemAccepts} ->
+ %% Reply {error, closed} to all pending accepts
+ lists:foreach(fun({_, _, AccFrom}) ->
+ gen_server:reply(AccFrom,
+ {error, closed})
+ end, DelAccepts),
+ {reply, ok,
+ St#st{cons = Cons, paccepts = RemAccepts}};
+ _ ->
+ {reply, ok, St#st{cons = Cons}}
+ end;
+ _ ->
+ {reply, ok, St}
+ end;
+
+%%
+%% listen
+%%
+handle_call({listen, Broker, IP, LPort, Flags, BackLog}, From, St) ->
+ debug(St, "listen: broker = ~w, IP = ~w, "
+ "sport = ~w~n", [Broker, IP, LPort]),
+ Port = St#st.port,
+ IPStr = ip_to_string(IP),
+ IntRef = new_intref(St),
+ send_cmd(Port, ?LISTEN, [int32(IntRef), int16(LPort), IPStr, 0,
+ int16(BackLog), Flags, 0]),
+ Cons = add({{intref, IntRef}, Broker, From}, St#st.cons),
+ %% We reply when we have got LISTEN_REP.
+ {noreply, St#st{cons = Cons, intref = IntRef}};
+
+%%
+%% peercert
+%%
+handle_call({peercert, Broker, Fd}, From, St) ->
+ debug(St, "peercert: broker = ~w, fd = ~w~n",
+ [Broker, Fd]),
+ case replace_from_by_fd(Fd, St#st.cons, From) of
+ {ok, _, Cons} ->
+ send_cmd(St#st.port, ?GETPEERCERT, [int32(Fd)]),
+ %% We reply when we get GETPEERCERT_REP or GETPEERCERT_ERR.
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ {reply, {error, ebadf}, St}
+ end;
+
+
+%%
+%% peername
+%%
+handle_call({peername, Broker, Fd}, From, St) ->
+ debug(St, "peername: broker = ~w, fd = ~w~n",
+ [Broker, Fd]),
+ case replace_from_by_fd(Fd, St#st.cons, From) of
+ {ok, _, Cons} ->
+ send_cmd(St#st.port, ?GETPEERNAME, [int32(Fd)]),
+ %% We reply when we get GETPEERNAME_REP or GETPEERNAME_ERR.
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ {reply, {error, ebadf}, St}
+ end;
+
+%%
+%% proxy join
+%%
+handle_call({proxy_join, Broker, Fd, LPort}, From, St) ->
+ debug(St, "proxy_join: broker = ~w, fd = ~w, "
+ "sport = ~w~n", [Broker, Fd, LPort]),
+ case replace_from_by_fd(Fd, St#st.cons, From) of
+ {ok, _, Cons} ->
+ send_cmd(St#st.port, ?PROXY_JOIN, [int32(Fd),
+ int16(LPort)]),
+ %% We reply when we get PROXY_JOIN_REP, or PROXY_JOIN_ERR.
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ {reply, {error, ebadf}, St}
+ end;
+
+%%
+%% seed
+%%
+handle_call({seed, Data}, _From, St) when is_binary(Data) ->
+ send_cmd(St#st.port, ?SET_SEED, [int32(byte_size(Data)), Data]),
+ {reply, ok, St};
+
+handle_call({seed, Data}, From, St) ->
+ case catch list_to_binary(Data) of
+ {'EXIT', _} ->
+ {reply, {error, edata}, St};
+ Bin ->
+ handle_call({seed, Bin}, From, St)
+ end;
+
+%%
+%% setnodelay
+%%
+handle_call({setnodelay, Broker, Fd, Boolean}, From, St) ->
+ debug(St, "setnodelay: broker = ~w, fd = ~w, "
+ "boolean = ~w~n", [Broker, Fd, Boolean]),
+ case replace_from_by_fd(Fd, St#st.cons, From) of
+ {ok, _, Cons} ->
+ Val = if Boolean == true -> 1; true -> 0 end,
+ send_cmd(St#st.port, ?SET_SOCK_OPT,
+ [int32(Fd), ?SET_TCP_NODELAY, Val]),
+ %% We reply when we get IOCTL_OK or IOCTL_ERR.
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ {reply, {error, ebadf}, St}
+ end;
+
+%%
+%% sockname
+%%
+handle_call({sockname, Broker, Fd}, From, St) ->
+ debug(St, "sockname: broker = ~w, fd = ~w~n",
+ [Broker, Fd]),
+ case replace_from_by_fd(Fd, St#st.cons, From) of
+ {ok, _, Cons} ->
+ send_cmd(St#st.port, ?GETSOCKNAME, [int32(Fd)]),
+ %% We reply when we get GETSOCKNAME_REP or GETSOCKNAME_ERR.
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ {reply, {error, ebadf}, St}
+ end;
+
+%%
+%% version
+%%
+handle_call(version, From, St) ->
+ debug(St, "version: from = ~w~n", [From]),
+ {reply, {ok, {St#st.compvsn, St#st.libvsn}}, St};
+
+%%
+%% dump
+%%
+handle_call({dump, Broker}, _From, St) ->
+ debug(St, "dump: broker = ~w", [Broker]),
+ Port = St#st.port,
+ send_cmd(Port, ?DUMP_CMD, []),
+ {reply, ok, St};
+
+%%
+%% set_debug
+%%
+handle_call({set_debug, Bool, Broker}, _From, St) ->
+ debug(St, "set_debug: broker = ~w", [Broker]),
+ Value = case Bool of
+ true ->
+ 1;
+ false ->
+ 0
+ end,
+ Port = St#st.port,
+ send_cmd(Port, ?DEBUG_CMD, [Value]),
+ {reply, ok, St};
+
+%%
+%% set_debugmsg
+%%
+handle_call({set_debugmsg, Bool, Broker}, _From, St) ->
+ debug(St, "set_debugmsg: broker = ~w", [Broker]),
+ Value = case Bool of
+ true ->
+ 1;
+ false ->
+ 0
+ end,
+ Port = St#st.port,
+ send_cmd(Port, ?DEBUGMSG_CMD, [Value]),
+ {reply, ok, St};
+
+handle_call(Request, _From, St) ->
+ debug(St, "unexpected call: ~w~n", [Request]),
+ Reply = {error, {badcall, Request}},
+ {reply, Reply, St}.
+
+%%
+%% handle_cast(Msg, St)
+%%
+
+
+handle_cast(Msg, St) ->
+ debug(St, "unexpected cast: ~w~n", [Msg]),
+ {noreply, St}.
+
+%%
+%% handle_info(Info, St)
+%%
+
+%% Data from port
+%%
+handle_info({Port, {data, Bin}},
+ #st{cons = StCons, paccepts = Paccepts,
+ port = Port, proxylsport = Proxylsport} = St)
+ when is_binary(Bin) ->
+ %% io:format("++++ ssl_server got from port: ~w~n", [Bin]),
+ <<OpCode:8, _/binary>> = Bin,
+ case OpCode of
+ %%
+ %% transport_accept
+ %%
+ ?TRANSPORT_ACCEPT_ERR when byte_size(Bin) >= 5 ->
+ {ListenFd, Reason} = decode_msg(Bin, [int32, atom]),
+ debug(St, "transport_accept_err: listenfd = ~w, "
+ "reason = ~w~n", [ListenFd, Reason]),
+ case delete_last_by_fd(ListenFd, Paccepts) of
+ {ok, {_, _, From}, PAccepts} ->
+ gen_server:reply(From, {error, Reason}),
+ {noreply, St#st{paccepts = PAccepts}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+ ?TRANSPORT_ACCEPT_REP when byte_size(Bin) >= 9 ->
+ {ListenFd, Fd} = decode_msg(Bin, [int32, int32]),
+ debug(St, "transport_accept_rep: listenfd = ~w, "
+ "fd = ~w~n", [ListenFd, Fd]),
+ case delete_last_by_fd(ListenFd, Paccepts) of
+ {ok, {_, Broker, From}, PAccepts} ->
+ Reply = {ok, Fd, Proxylsport},
+ gen_server:reply(From, Reply),
+ debug(St, "transport_accept_rep: From = ~w\n", [From]),
+ Cons = add({Fd, Broker, From}, StCons),
+ {noreply, St#st{cons = Cons, paccepts = PAccepts}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+
+ %%
+ %% ssl_accept
+ %%
+ ?SSL_ACCEPT_ERR when byte_size(Bin) >= 5 ->
+ {Fd, Reason} = decode_msg(Bin, [int32, atom]),
+ debug(St, "ssl_accept_err: listenfd = ~w, "
+ "reason = ~w~n", [Fd, Reason]),
+ %% JC: remove this?
+ case delete_last_by_fd(Fd, StCons) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {error, Reason}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+ ?SSL_ACCEPT_REP when byte_size(Bin) >= 5 ->
+ Fd = decode_msg(Bin, [int32]),
+ debug(St, "ssl_accept_rep: Fd = ~w\n", [Fd]),
+ case replace_from_by_fd(Fd, StCons, []) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, ok),
+ {noreply, St#st{cons = Cons}};
+ _ ->
+ {noreply, St}
+ end;
+
+ %%
+ %% connect
+ %%
+ ?CONNECT_SYNC_ERR when byte_size(Bin) >= 5 ->
+ {IntRef, Reason} = decode_msg(Bin, [int32, atom]),
+ debug(St, "connect_sync_err: intref = ~w, "
+ "reason = ~w~n", [IntRef, Reason]),
+ case delete_by_intref(IntRef, StCons) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {error, Reason}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ {noreply, St}
+ end;
+ ?CONNECT_WAIT when byte_size(Bin) >= 9 ->
+ {IntRef, Fd} = decode_msg(Bin, [int32, int32]),
+ debug(St, "connect_wait: intref = ~w, "
+ "fd = ~w~n", [IntRef, Fd]),
+ case replace_fd_by_intref(IntRef, StCons, Fd) of
+ {ok, _, Cons} ->
+ %% We reply when we get CONNECT_REP or CONNECT_ERR
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% We have a new Fd which must be closed
+ send_cmd(Port, ?CLOSE, int32(Fd)),
+ {noreply, St}
+ end;
+ ?CONNECT_REP when byte_size(Bin) >= 5 ->
+ %% after CONNECT_WAIT
+ Fd = decode_msg(Bin, [int32]),
+ debug(St, "connect_rep: fd = ~w~n", [Fd]),
+ case replace_from_by_fd(Fd, StCons, []) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {ok, Fd, Proxylsport}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ {noreply, St}
+ end;
+ ?CONNECT_ERR when byte_size(Bin) >= 5 ->
+ {Fd, Reason} = decode_msg(Bin, [int32, atom]),
+ debug(St, "connect_err: fd = ~w, "
+ "reason = ~w~n", [Fd, Reason]),
+ case delete_by_fd(Fd, StCons) of
+ {ok, {_, _, From}, Cons} ->
+ %% Fd not yet published - hence close ourselves
+ send_cmd(Port, ?CLOSE, int32(Fd)),
+ gen_server:reply(From, {error, Reason}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+
+ %%
+ %% connection_info
+ %%
+ ?GETCONNINFO_REP when byte_size(Bin) >= 5 ->
+ {Fd, Protocol, Cipher} = decode_msg(Bin, [int32, string, string]),
+ debug(St, "connection_info_rep: fd = ~w, "
+ "protcol = ~p, ip = ~p~n", [Fd, Protocol, Cipher]),
+ case replace_from_by_fd(Fd, StCons, []) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {ok, {protocol_name(Protocol),
+ Cipher}}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+ ?GETCONNINFO_ERR when byte_size(Bin) >= 5 ->
+ {Fd, Reason} = decode_msg(Bin, [int32, atom]),
+ debug(St, "connection_info_err: fd = ~w, "
+ "reason = ~w~n", [Fd, Reason]),
+ case replace_from_by_fd(Fd, StCons, []) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {error, Reason}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+
+ %%
+ %% listen
+ %%
+ ?LISTEN_SYNC_ERR when byte_size(Bin) >= 5 ->
+ {IntRef, Reason} = decode_msg(Bin, [int32, atom]),
+ debug(St, "listen_sync_err: intref = ~w, "
+ "reason = ~w~n", [IntRef, Reason]),
+ case delete_by_intref(IntRef, StCons) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {error, Reason}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ {noreply, St}
+ end;
+ ?LISTEN_REP when byte_size(Bin) >= 11 ->
+ {IntRef, ListenFd, LPort} = decode_msg(Bin, [int32, int32, int16]),
+ debug(St, "listen_rep: intref = ~w, "
+ "listenfd = ~w, sport = ~w~n", [IntRef, ListenFd, LPort]),
+ case replace_fd_from_by_intref(IntRef, StCons, ListenFd, []) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {ok, ListenFd, LPort}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% ListenFd has to be closed.
+ send_cmd(Port, ?CLOSE, int32(ListenFd)),
+ {noreply, St}
+ end;
+
+ %%
+ %% proxy join
+ %%
+ ?PROXY_JOIN_REP when byte_size(Bin) >= 5 ->
+ Fd = decode_msg(Bin, [int32]),
+ debug(St, "proxy_join_rep: fd = ~w~n",
+ [Fd]),
+ case get_by_fd(Fd, StCons) of
+ {ok, {_, _, From}} ->
+ gen_server:reply(From, ok),
+ {noreply, St};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+ ?PROXY_JOIN_ERR when byte_size(Bin) >= 5 ->
+ {Fd, Reason} = decode_msg(Bin, [int32, atom]),
+ debug(St, "proxy_join_rep: fd = ~w, "
+ "reason = ~w~n", [Fd, Reason]),
+ case delete_by_fd(Fd, StCons) of
+ {ok, {_, _, From}, Cons} ->
+ case Reason of
+ enoproxysocket ->
+ send_cmd(Port, ?CLOSE, int32(Fd));
+ _ ->
+ ok
+ %% Must not close Fd since it is published
+ end,
+ gen_server:reply(From, {error, Reason}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+
+ %%
+ %% peername
+ %%
+ ?GETPEERNAME_REP when byte_size(Bin) >= 5 ->
+ {Fd, LPort, IPString} = decode_msg(Bin, [int32, int16, string]),
+ debug(St, "getpeername_rep: fd = ~w, "
+ "sport = ~w, ip = ~p~n", [Fd, LPort, IPString]),
+ case replace_from_by_fd(Fd, StCons, []) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {ok, {IPString, LPort}}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+ ?GETPEERNAME_ERR when byte_size(Bin) >= 5 ->
+ {Fd, Reason} = decode_msg(Bin, [int32, atom]),
+ debug(St, "getpeername_err: fd = ~w, "
+ "reason = ~w~n", [Fd, Reason]),
+ case replace_from_by_fd(Fd, StCons, []) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {error, Reason}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+
+ %%
+ %% ioctl
+ %%
+ ?IOCTL_OK when byte_size(Bin) >= 5 ->
+ Fd = decode_msg(Bin, [int32]),
+ debug(St, "ioctl_ok: fd = ~w~n",
+ [Fd]),
+ case replace_from_by_fd(Fd, StCons, []) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, ok),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+ ?IOCTL_ERR when byte_size(Bin) >= 5 ->
+ {Fd, Reason} = decode_msg(Bin, [int32, atom]),
+ debug(St, "ioctl_err: fd = ~w, "
+ "reason = ~w~n", [Fd, Reason]),
+ case replace_from_by_fd(Fd, StCons, []) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {error, Reason}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+
+ %%
+ %% sockname
+ %%
+ ?GETSOCKNAME_REP when byte_size(Bin) >= 5 ->
+ {Fd, LPort, IPString} = decode_msg(Bin, [int32, int16, string]),
+ debug(St, "getsockname_rep: fd = ~w, "
+ "sport = ~w, ip = ~p~n", [Fd, LPort, IPString]),
+ case replace_from_by_fd(Fd, StCons, []) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {ok, {IPString, LPort}}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+ ?GETSOCKNAME_ERR when byte_size(Bin) >= 5 ->
+ {Fd, Reason} = decode_msg(Bin, [int32, atom]),
+ debug(St, "getsockname_err: fd = ~w, "
+ "reason = ~w~n", [Fd, Reason]),
+ case replace_from_by_fd(Fd, StCons, []) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {error, Reason}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+
+ %%
+ %% peercert
+ %%
+ ?GETPEERCERT_REP when byte_size(Bin) >= 5 ->
+ {Fd, Cert} = decode_msg(Bin, [int32, bin]),
+ debug(St, "getpeercert_rep: fd = ~w~n", [Fd]),
+ case replace_from_by_fd(Fd, StCons, []) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {ok, Cert}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end;
+ ?GETPEERCERT_ERR when byte_size(Bin) >= 5 ->
+ {Fd, Reason} = decode_msg(Bin, [int32, atom]),
+ debug(St, "getpeercert_err: fd = ~w, reason = ~w~n",
+ [Fd, Reason]),
+ case replace_from_by_fd(Fd, StCons, []) of
+ {ok, {_, _, From}, Cons} ->
+ gen_server:reply(From, {error, Reason}),
+ {noreply, St#st{cons = Cons}};
+ _Other ->
+ %% Already closed
+ {noreply, St}
+ end
+ end;
+
+%%
+%% EXIT
+%%
+handle_info({'EXIT', Pid, Reason}, St) when is_pid(Pid) ->
+ debug(St, "exit pid = ~w, "
+ "reason = ~w~n", [Pid, Reason]),
+ case delete_by_pid(Pid, St#st.cons) of
+ {ok, {{intref, _}, Pid, _}, Cons} ->
+ {noreply, St#st{cons = Cons}};
+ {ok, {Fd, Pid, _}, Cons} ->
+ send_cmd(St#st.port, ?CLOSE, int32(Fd)),
+ %% If Fd is a listen socket fd, there might be pending
+ %% accepts for that fd.
+ case delete_all_by_fd(Fd, St#st.paccepts) of
+ {ok, DelAccepts, RemAccepts} ->
+ %% Reply {error, closed} to all pending accepts.
+ lists:foreach(fun({_, _, From}) ->
+ gen_server:reply(From,
+ {error, closed})
+ end, DelAccepts),
+ {noreply,
+ St#st{cons = Cons, paccepts = RemAccepts}};
+ _ ->
+ {noreply, St#st{cons = Cons}}
+ end;
+ _ ->
+ case delete_by_pid(Pid, St#st.paccepts) of
+ {ok, {ListenFd, _, _}, PAccepts} ->
+ %% decrement ref count in port program
+ send_cmd(St#st.port, ?NOACCEPT, int32(ListenFd)),
+ {noreply, St#st{paccepts = PAccepts}};
+ _ ->
+ {noreply, St}
+ end
+ end;
+
+%%
+%% 'badsig' means bad message to port. Port program is unaffected.
+%%
+handle_info({'EXIT', Port, badsig}, #st{port = Port} = St) ->
+ debug(St, "badsig!!!~n", []),
+ {noreply, St};
+
+handle_info({'EXIT', Port, Reason}, #st{port = Port} = St) ->
+ {stop, Reason, St};
+
+handle_info(Info, St) ->
+ debug(St, "unexpected info: ~w~n", [Info]),
+ {noreply, St}.
+
+%%
+%% terminate(Reason, St) -> any
+%%
+terminate(_Reason, _St) ->
+ ok.
+
+%%
+%% code_change(OldVsn, St, Extra) -> {ok, NSt}
+%%
+code_change(_OldVsn, St, _Extra) ->
+ {ok, St}.
+
+%%%----------------------------------------------------------------------
+%%% Internal functions
+%%%----------------------------------------------------------------------
+
+%%
+%% Send binary command to sock
+%%
+send_cmd(Port, Cmd, Args) ->
+ Port ! {self(), {command, [Cmd| Args]}}.
+
+%%
+%% add(Descr, Cons) -> NCons
+%%
+add(D, L) ->
+ [D| L].
+
+%%
+%% get_by_fd(Fd, Cons) -> {ok, Descr} | not_found
+%%
+get_by_fd(Fd, Cons) ->
+ get_by_pos(Fd, 1, Cons).
+
+%%
+%% delete_by_fd(Fd, Cons) -> {ok, OldDesc, NewCons} | not_found.
+%%
+delete_by_fd(Fd, Cons) ->
+ delete_by_pos(Fd, 1, Cons).
+
+%%
+%% delete_all_by_fd(Fd, Cons) -> {ok, DelCons, RemCons} | not_found.
+%%
+delete_all_by_fd(Fd, Cons) ->
+ delete_all_by_pos(Fd, 1, Cons).
+
+%%
+%% delete_by_intref(IntRef, Cons) -> {ok, OldDesc, NewCons} | not_found.
+%%
+delete_by_intref(IntRef, Cons) ->
+ delete_by_pos({intref, IntRef}, 1, Cons).
+
+%%
+%% delete_by_pid(Pid, Cons) -> {ok, OldDesc, NewCons} | not_found.
+%%
+delete_by_pid(Pid, Cons) ->
+ delete_by_pos(Pid, 2, Cons).
+
+%%
+%% delete_last_by_fd(Fd, Cons) -> {ok, OldDesc, NCons} | not_found
+%%
+delete_last_by_fd(Fd, Cons) ->
+ case dlbf(Fd, Cons) of
+ {X, L} ->
+ {ok, X, L};
+ _Other ->
+ not_found
+ end.
+
+dlbf(Fd, [H]) ->
+ last_elem(Fd, H, []);
+dlbf(Fd, [H|T]) ->
+ case dlbf(Fd, T) of
+ {X, L} ->
+ {X, [H|L]};
+ L ->
+ last_elem(Fd, H, L)
+ end;
+dlbf(_Fd, []) ->
+ [].
+
+last_elem(Fd, H, L) when element(1, H) == Fd ->
+ {H, L};
+last_elem(_, H, L) ->
+ [H|L].
+
+
+%%
+%% replace_from_by_fd(Fd, Cons, From) -> {ok, OldDesc, NewList} | not_found
+%%
+replace_from_by_fd(Fd, Cons, From) ->
+ replace_posn_by_pos(Fd, 1, Cons, [{From, 3}]).
+
+%%
+%% replace_fd_by_intref(IntRef, Cons, Fd) -> {ok, OldDesc, NewList} | not_f.
+%%
+replace_fd_by_intref(IntRef, Cons, Fd) ->
+ replace_posn_by_pos({intref, IntRef}, 1, Cons, [{Fd, 1}]).
+
+%%
+%% replace_fd_from_by_intref(IntRef, Cons, NFd, From) ->
+%% {ok, OldDesc, NewList} | not_found
+%%
+replace_fd_from_by_intref(IntRef, Cons, NFd, From) ->
+ replace_posn_by_pos({intref, IntRef}, 1, Cons, [{NFd, 1}, {From, 3}]).
+
+
+%%
+%% All *_by_pos functions
+%%
+
+get_by_pos(Key, Pos, [H|_]) when element(Pos, H) == Key ->
+ {ok, H};
+get_by_pos(Key, Pos, [_|T]) ->
+ get_by_pos(Key, Pos, T);
+get_by_pos(_, _, []) ->
+ not_found.
+
+delete_by_pos(Key, Pos, Cons) ->
+ case delete_by_pos1(Key, Pos, {not_found, Cons}) of
+ {not_found, _} ->
+ not_found;
+ {ODesc, NCons} ->
+ {ok, ODesc, NCons}
+ end.
+delete_by_pos1(Key, Pos, {_R, [H|T]}) when element(Pos, H) == Key ->
+ {H, T};
+delete_by_pos1(Key, Pos, {R, [H|T]}) ->
+ {R0, T0} = delete_by_pos1(Key, Pos, {R, T}),
+ {R0, [H| T0]};
+delete_by_pos1(_, _, {R, []}) ->
+ {R, []}.
+
+delete_all_by_pos(Key, Pos, Cons) ->
+ case lists:foldl(fun(H, {Ds, Rs}) when element(Pos, H) == Key ->
+ {[H|Ds], Rs};
+ (H, {Ds, Rs}) ->
+ {Ds, [H|Rs]}
+ end, {[], []}, Cons) of
+ {[], _} ->
+ not_found;
+ {DelCons, RemCons} ->
+ {ok, DelCons, RemCons}
+ end.
+
+replace_posn_by_pos(Key, Pos, Cons, Repls) ->
+ replace_posn_by_pos1(Key, Pos, Cons, Repls, []).
+
+replace_posn_by_pos1(Key, Pos, [H0| T], Repls, Acc)
+ when element(Pos, H0) =:= Key ->
+ H = lists:foldl(fun({Val, VPos}, Tuple) ->
+ setelement(VPos, Tuple, Val)
+ end, H0, Repls),
+ {ok, H0, lists:reverse(Acc, [H| T])};
+replace_posn_by_pos1(Key, Pos, [H|T], Repls, Acc) ->
+ replace_posn_by_pos1(Key, Pos, T, Repls, [H| Acc]);
+replace_posn_by_pos1(_, _, [], _, _) ->
+ not_found.
+
+%%
+%% Binary/integer conversions
+%%
+int16(I) ->
+ %%[(I bsr 8) band 255, I band 255].
+ <<I:16>>.
+
+int32(I) ->
+ %% [(I bsr 24) band 255,
+ %% (I bsr 16) band 255,
+ %% (I bsr 8) band 255,
+ %% I band 255].
+ <<I:32>>.
+
+%% decode_msg(Bin, Format) -> Tuple | integer() | atom() | string() |
+%% list of binaries()
+%%
+%% Decode message from binary
+%% Format = [spec()]
+%% spec() = int16 | int32 | string | atom | bin | bins
+%%
+%% Notice: The first byte (op code) of the binary message is removed.
+%% Notice: bins returns a *list* of binaries.
+%%
+decode_msg(<<_, Bin/binary>>, Format) ->
+ Dec = dec(Format, Bin),
+ case Dec of
+ [Dec1] -> Dec1;
+ _ -> list_to_tuple(Dec)
+ end.
+
+dec([], _) ->
+ [];
+dec([int16| F], <<N:16, Bin/binary>>) ->
+ [N| dec(F, Bin)];
+dec([int32| F], <<N:32, Bin/binary>>) ->
+ [N| dec(F, Bin)];
+dec([string| F], Bin0) ->
+ {Cs, Bin1} = dec_string(Bin0),
+ [Cs| dec(F, Bin1)];
+dec([atom|F], Bin0) ->
+ {Cs, Bin1} = dec_string(Bin0),
+ [list_to_atom(Cs)| dec(F, Bin1)];
+
+dec([bin|F], Bin) ->
+ {Bin1, Bin2} = dec_bin(Bin),
+ [Bin1| dec(F, Bin2)].
+
+%% NOTE: This clause is not actually used yet.
+%% dec([bins|F], <<N:32, Bin0/binary>>) ->
+%% {Bins, Bin1} = dec_bins(N, Bin0),
+%% [Bins| dec(F, Bin1)].
+
+dec_string(Bin) ->
+ dec_string(Bin, []).
+
+dec_string(<<0, Bin/binary>>, RCs) ->
+ {lists:reverse(RCs), Bin};
+dec_string(<<C, Bin/binary>>, RCs) ->
+ dec_string(Bin, [C| RCs]).
+
+dec_bin(<<L:32, Bin0/binary>>) ->
+ <<Bin1:L/binary, Bin2/binary>> = Bin0,
+ {Bin1, Bin2}.
+
+%% dec_bins(N, Bin) ->
+%% dec_bins(N, Bin, []).
+
+%% dec_bins(0, Bin, Acc) ->
+%% {lists:reverse(Acc), Bin};
+%% dec_bins(N, Bin0, Acc) when N > 0 ->
+%% {Bin1, Bin2} = dec_bin(Bin0),
+%% dec_bins(N - 1, Bin2, [Bin1| Acc]).
+
+%%
+%% new_intref
+%%
+new_intref(St) ->
+ (St#st.intref + 1) band 16#ffffffff.
+
+%%
+%% {Program, Flags} = mk_cmd_line(DefaultProgram)
+%%
+mk_cmd_line(Default) ->
+ {port_program(Default),
+ lists:flatten([debug_flag(), " ", debug_port_flag(), " ",
+ debugdir_flag(), " ",
+ msgdebug_flag(), " ", proxylsport_flag(), " ",
+ proxybacklog_flag(), " ", ephemeral_rsa_flag(), " ",
+ ephemeral_dh_flag(), " ",
+ protocol_version_flag(), " "])}.
+
+port_program(Default) ->
+ case application:get_env(ssl, port_program) of
+ {ok, Program} when is_list(Program) ->
+ Program;
+ _Other ->
+ Default
+ end.
+
+%%
+%% As this server may be started by the distribution, it is not safe to assume
+%% a working code server, neither a working file server.
+%% I try to utilize the most primitive interfaces available to determine
+%% the directory of the port_program.
+%%
+find_priv_bin() ->
+ PrivDir = case (catch code:priv_dir(ssl)) of
+ {'EXIT', _} ->
+ %% Code server probably not startet yet
+ {ok, P} = erl_prim_loader:get_path(),
+ ModuleFile = atom_to_list(?MODULE) ++ extension(),
+ Pd = (catch lists:foldl
+ (fun(X,Acc) ->
+ M = filename:join([X, ModuleFile]),
+ %% The file server probably not started
+ %% either, has to use raw interface.
+ case file:raw_read_file_info(M) of
+ {ok,_} ->
+ %% Found our own module in the
+ %% path, lets bail out with
+ %% the priv_dir of this directory
+ Y = filename:split(X),
+ throw(filename:join
+ (lists:sublist
+ (Y,length(Y) - 1)
+ ++ ["priv"]));
+ _ ->
+ Acc
+ end
+ end,
+ false,P)),
+ case Pd of
+ false ->
+ exit(ssl_priv_dir_indeterminate);
+ _ ->
+ Pd
+ end;
+ Dir ->
+ Dir
+ end,
+ filename:join([PrivDir, "bin"]).
+
+extension() ->
+ %% erlang:info(machine) returns machine name as text in all uppercase
+ "." ++ string:to_lower(erlang:system_info(machine)).
+
+debug_flag() ->
+ case os:getenv("ERL_SSL_DEBUG") of
+ false ->
+ get_env(debug, "-d");
+ _ ->
+ "-d"
+ end.
+
+debug_port_flag() ->
+ case os:getenv("ERL_SSL_DEBUGPORT") of
+ false ->
+ get_env(debug, "-d");
+ _ ->
+ "-d"
+ end.
+
+msgdebug_flag() ->
+ case os:getenv("ERL_SSL_MSGDEBUG") of
+ false ->
+ get_env(msgdebug, "-dm");
+ _ ->
+ "-dm"
+ end.
+
+proxylsport_flag() ->
+ case application:get_env(ssl, proxylsport) of
+ {ok, PortNum} ->
+ "-pp " ++ integer_to_list(PortNum);
+ _Other ->
+ ""
+ end.
+
+proxybacklog_flag() ->
+ case application:get_env(ssl, proxylsbacklog) of
+ {ok, Size} ->
+ "-pb " ++ integer_to_list(Size);
+ _Other ->
+ ""
+ end.
+
+debugdir_flag() ->
+ case os:getenv("ERL_SSL_DEBUG") of
+ false ->
+ case application:get_env(ssl, debugdir) of
+ {ok, Dir} when is_list(Dir) ->
+ "-dd " ++ Dir;
+ _Other ->
+ ""
+ end;
+ _ ->
+ "-dd ./"
+ end.
+
+ephemeral_rsa_flag() ->
+ case application:get_env(ssl, ephemeral_rsa) of
+ {ok, true} ->
+ "-ersa ";
+ _Other ->
+ ""
+ end.
+
+ephemeral_dh_flag() ->
+ case application:get_env(ssl, ephemeral_dh) of
+ {ok, true} ->
+ "-edh ";
+ _Other ->
+ ""
+ end.
+
+protocol_version_flag() ->
+ case application:get_env(ssl, protocol_version) of
+ {ok, []} ->
+ "";
+ {ok, Vsns} when is_list(Vsns) ->
+ case transform_vsns(Vsns) of
+ N when (N > 0) ->
+ "-pv " ++ integer_to_list(N);
+ _ ->
+ ""
+ end;
+ _Other ->
+ ""
+ end.
+
+transform_vsns(Vsns) ->
+ transform_vsns(Vsns, 0).
+
+transform_vsns([sslv2| Vsns], I) ->
+ transform_vsns(Vsns, I bor ?SSLv2);
+transform_vsns([sslv3| Vsns], I) ->
+ transform_vsns(Vsns, I bor ?SSLv3);
+transform_vsns([tlsv1| Vsns], I) ->
+ transform_vsns(Vsns, I bor ?TLSv1);
+transform_vsns([_ | Vsns], I) ->
+ transform_vsns(Vsns, I);
+transform_vsns([], I) ->
+ I.
+
+protocol_name("SSLv2") -> sslv2;
+protocol_name("SSLv3") -> sslv3;
+protocol_name("TLSv1") -> tlsv1.
+
+get_env(Key, Val) ->
+ case application:get_env(ssl, Key) of
+ {ok, true} ->
+ Val;
+ _Other ->
+ ""
+ end.
+
+ip_to_string({A,B,C,D}) ->
+ [integer_to_list(A),$.,integer_to_list(B),$.,
+ integer_to_list(C),$.,integer_to_list(D)].
+
+debug(St, Format, Args) ->
+ debug1(St#st.debug, Format, Args).
+
+debug1(true, Format0, Args) ->
+ {_MS, S, MiS} = erlang:now(),
+ Secs = S rem 100,
+ MiSecs = MiS div 1000,
+ Format = "++++ ~3..0w:~3..0w ssl_server (~w): " ++ Format0,
+ io:format(Format, [Secs, MiSecs, self()| Args]);
+debug1(_, _, _) ->
+ ok.
diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl
new file mode 100644
index 0000000000..bcb10daf69
--- /dev/null
+++ b/lib/ssl/src/ssl_session.erl
@@ -0,0 +1,172 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Handles ssl sessions
+%%----------------------------------------------------------------------
+
+-module(ssl_session).
+
+-include("ssl_handshake.hrl").
+-include("ssl_internal.hrl").
+
+%% Internal application API
+-export([is_new/2, id/3, id/6, valid_session/2]).
+
+-define(GEN_UNIQUE_ID_MAX_TRIES, 10).
+
+%%--------------------------------------------------------------------
+%% Function: is_new(ClientSuggestedId, ServerDecidedId) -> true | false
+%%
+%% ClientSuggestedId = binary()
+%% ServerDecidedId = binary()
+%%
+%% Description: Checks if the session id decided by the server is a
+%% new or resumed sesion id.
+%%--------------------------------------------------------------------
+is_new(<<>>, _) ->
+ true;
+is_new(SessionId, SessionId) ->
+ false;
+is_new(_, _) ->
+ true.
+
+%%--------------------------------------------------------------------
+%% Function: id(ClientInfo, Cache, CacheCb) -> SessionId
+%%
+%% ClientInfo = {HostIP, Port, SslOpts}
+%% HostIP = ipadress()
+%% Port = integer()
+%% CacheCb = atom()
+%% SessionId = binary()
+%%
+%% Description: Should be called by the client side to get an id
+%% for the client hello message.
+%%--------------------------------------------------------------------
+id(ClientInfo, Cache, CacheCb) ->
+ case select_session(ClientInfo, Cache, CacheCb) of
+ no_session ->
+ <<>>;
+ SessionId ->
+ SessionId
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: id(Port, SuggestedSessionId, ReuseFun, CacheCb,
+%% SecondLifeTime) -> SessionId
+%%
+%% Port = integer()
+%% SuggestedSessionId = SessionId = binary()
+%% ReuseFun = fun(SessionId, PeerCert, Compression, CipherSuite) ->
+%% true | false
+%% CacheCb = atom()
+%%
+%% Description: Should be called by the server side to get an id
+%% for the server hello message.
+%%--------------------------------------------------------------------
+id(Port, <<>>, _, Cache, CacheCb, _) ->
+ new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb);
+
+id(Port, SuggestedSessionId, #ssl_options{reuse_sessions = ReuseEnabled,
+ reuse_session = ReuseFun},
+ Cache, CacheCb, SecondLifeTime) ->
+ case is_resumable(SuggestedSessionId, Port, ReuseEnabled,
+ ReuseFun, Cache, CacheCb, SecondLifeTime) of
+ true ->
+ SuggestedSessionId;
+ false ->
+ new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb)
+ end.
+%%--------------------------------------------------------------------
+%% Function: valid_session(Session, LifeTime) -> true | false
+%%
+%% Session = #session{}
+%% LifeTime = integer() - seconds
+%%
+%% Description: Check that the session has not expired
+%%--------------------------------------------------------------------
+valid_session(#session{time_stamp = TimeStamp}, LifeTime) ->
+ Now = calendar:datetime_to_gregorian_seconds({date(), time()}),
+ Now - TimeStamp < LifeTime.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+select_session({HostIP, Port, SslOpts}, Cache, CacheCb) ->
+ Sessions = CacheCb:select_session(Cache, {HostIP, Port}),
+ select_session(Sessions, SslOpts).
+
+select_session([], _) ->
+ no_session;
+
+select_session(Sessions, #ssl_options{ciphers = Ciphers,
+ reuse_sessions = ReuseSession}) ->
+ IsResumable =
+ fun(Session) ->
+ ReuseSession andalso (Session#session.is_resumable) andalso
+ lists:member(Session#session.cipher_suite, Ciphers)
+ end,
+ case [Id || [Id, Session] <- Sessions, IsResumable(Session)] of
+ [] ->
+ no_session;
+ 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
+%% states : "If we can not find a session id in
+%% ?GEN_UNIQUE_ID_MAX_TRIES either the RAND code is broken or someone
+%% is trying to open roughly very close to 2^128 (or 2^256) SSL
+%% sessions to our server"
+new_id(_, 0, _, _) ->
+ <<>>;
+new_id(Port, Tries, Cache, CacheCb) ->
+ Id = crypto:rand_bytes(?NUM_OF_SESSION_ID_BYTES),
+ case CacheCb:lookup(Cache, {Port, Id}) of
+ undefined ->
+ Now = calendar:datetime_to_gregorian_seconds({date(), time()}),
+ %% New sessions can not be set to resumable
+ %% until handshake is compleate and the
+ %% other session values are set.
+ CacheCb:update(Cache, {Port, Id}, #session{session_id = Id,
+ is_resumable = false,
+ time_stamp = Now}),
+ Id;
+ _ ->
+ new_id(Port, Tries - 1, Cache, CacheCb)
+ end.
+
+is_resumable(SuggestedSessionId, Port, ReuseEnabled, ReuseFun, Cache,
+ CacheCb, SecondLifeTime) ->
+ case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of
+ #session{cipher_suite = CipherSuite,
+ compression_method = Compression,
+ is_resumable = Is_resumable,
+ peer_certificate = PeerCert} = Session ->
+ ReuseEnabled
+ andalso Is_resumable
+ andalso valid_session(Session, SecondLifeTime)
+ andalso ReuseFun(SuggestedSessionId, PeerCert,
+ Compression, CipherSuite);
+ undefined ->
+ false
+ end.
diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl
new file mode 100644
index 0000000000..4a60892235
--- /dev/null
+++ b/lib/ssl/src/ssl_session_cache.erl
@@ -0,0 +1,124 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-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_session_cache).
+
+-behaviour(ssl_session_cache_api).
+
+-export([init/0, terminate/1, lookup/2, update/3, delete/2, foldl/3,
+ select_session/2]).
+
+%%--------------------------------------------------------------------
+%% Function: init() -> Cache
+%%
+%% Cache - Reference to the cash (opaque)
+%%
+%% Description: Return table reference. Called by ssl_manager process.
+%%--------------------------------------------------------------------
+init() ->
+ ets:new(cache_name(), [set, protected]).
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Cache) ->
+%%
+%% Cache - as returned by create/0
+%%
+%% Description: Handles cache table at termination of ssl manager.
+%%--------------------------------------------------------------------
+terminate(Cache) ->
+ ets:delete(Cache).
+
+%%--------------------------------------------------------------------
+%% Function: lookup(Cache, Key) -> Session | undefined
+%% Cache - as returned by create/0
+%% Session = #session{}
+%%
+%% Description: Looks up a cach entry. Should be callable from any
+%% process.
+%%--------------------------------------------------------------------
+lookup(Cache, Key) ->
+ case ets:lookup(Cache, Key) of
+ [{Key, Session}] ->
+ Session;
+ [] ->
+ undefined
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: update(Cache, Key, Session) -> _
+%% Cache - as returned by create/0
+%% Session = #session{}
+%%
+%% Description: Caches a new session or updates a already cached one.
+%% Will only be called from the ssl_manager process.
+%%--------------------------------------------------------------------
+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 =
+%%
+%% Description: Delets a cache entry.
+%% Will only be called from the ssl_manager process.
+%%--------------------------------------------------------------------
+delete(Cache, Key) ->
+ ets:delete(Cache, Key).
+
+%%--------------------------------------------------------------------
+%% Function: foldl(Fun, Acc0, Cache) -> Acc
+%%
+%% Fun - fun()
+%% Acc0 - term()
+%% Cache - cache_ref()
+%%
+%%
+%% 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
+%%--------------------------------------------------------------------
+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}
+%%
+%% Description: Selects a session that could be reused. Should be callable
+%% from any process.
+%%--------------------------------------------------------------------
+select_session(Cache, PartialKey) ->
+ ets:select(Cache,
+ [{{{PartialKey,'$1'}, '$2'},[],['$$']}]).
+
+%%--------------------------------------------------------------------
+%%% 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
new file mode 100644
index 0000000000..d2e846e9fd
--- /dev/null
+++ b/lib/ssl/src/ssl_session_cache_api.erl
@@ -0,0 +1,37 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-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_session_cache_api).
+
+-export([behaviour_info/1]).
+
+behaviour_info(callbacks) ->
+ [
+ {init, 0},
+ {terminate, 1},
+ {lookup, 2},
+ {update, 3},
+ {delete, 2},
+ {foldl, 3},
+ {select_session, 2}
+ ];
+behaviour_info(_) ->
+ undefined.
diff --git a/lib/ssl/src/ssl_ssl2.erl b/lib/ssl/src/ssl_ssl2.erl
new file mode 100644
index 0000000000..b1005b1acb
--- /dev/null
+++ b/lib/ssl/src/ssl_ssl2.erl
@@ -0,0 +1,37 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Handles sslv2 hello as clients supporting sslv2 and higher
+%% will send a sslv2 hello.
+%%----------------------------------------------------------------------
+
+-module(ssl_ssl2).
+
+-export([client_random/2]).
+
+client_random(ChallengeData, 32) ->
+ ChallengeData;
+client_random(ChallengeData, N) when N > 32 ->
+ <<NewChallengeData:32/binary, _/binary>> = ChallengeData,
+ NewChallengeData;
+client_random(ChallengeData, N) ->
+ Pad = list_to_binary(lists:duplicate(N, 0)),
+ <<Pad/binary, ChallengeData/binary>>.
diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl
new file mode 100644
index 0000000000..ab29ac64df
--- /dev/null
+++ b/lib/ssl/src/ssl_ssl3.erl
@@ -0,0 +1,286 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Handles sslv3 encryption.
+%%----------------------------------------------------------------------
+
+-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,
+ suites/0]).
+-compile(inline).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
+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 +
+ %% ServerHello.random +
+ %% ClientHello.random)) +
+ %% MD5(master_secret + SHA(`BB' + master_secret +
+ %% ServerHello.random +
+ %% ClientHello.random)) +
+ %% MD5(master_secret + SHA(`CCC' + master_secret +
+ %% ServerHello.random +
+ %% ClientHello.random)) + [...];
+ B = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48),
+ ?DBG_HEX(B),
+ B.
+
+finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
+ %% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished
+ %% struct {
+ %% opaque md5_hash[16];
+ %% opaque sha_hash[20];
+ %% } Finished;
+ %%
+ %% md5_hash MD5(master_secret + pad2 +
+ %% MD5(handshake_messages + Sender +
+ %% master_secret + pad1));
+ %% sha_hash SHA(master_secret + pad2 +
+ %% SHA(handshake_messages + Sender +
+ %% master_secret + pad1));
+ Sender = get_sender(Role),
+ MD5 = handshake_hash(?MD5, MasterSecret, Sender, MD5Hash),
+ 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 ->
+ %% md5_hash
+ %% MD5(master_secret + pad_2 +
+ %% MD5(handshake_messages + master_secret + pad_1));
+ %% sha_hash
+ %% SHA(master_secret + pad_2 +
+ %% SHA(handshake_messages + master_secret + pad_1));
+
+ MD5 = handshake_hash(?MD5, MasterSecret, undefined, MD5Hash),
+ SHA = handshake_hash(?SHA, MasterSecret, undefined, SHAHash),
+ <<MD5/binary, SHA/binary>>;
+
+certificate_verify(Algorithm, MasterSecret, {_, SHAHash})
+ when Algorithm == dh_dss; Algorithm == dhe_dss ->
+ %% sha_hash
+ %% SHA(master_secret + pad_2 +
+ %% SHA(handshake_messages + master_secret + pad_1));
+ handshake_hash(?SHA, MasterSecret, undefined, SHAHash).
+
+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 +
+ %% hash(MAC_write_secret + pad_1 + seq_num +
+ %% SSLCompressed.type + SSLCompressed.length +
+ %% SSLCompressed.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 ->
+ KeyBlock = generate_keyblock(MasterSecret, ServerRandom, ClientRandom,
+ 2*(HS+KML+IVS)),
+ %% draft-ietf-tls-ssl-version3-00 - 6.2.2
+ %% The key_block is partitioned as follows.
+ %% client_write_MAC_secret[CipherSpec.hash_size]
+ %% server_write_MAC_secret[CipherSpec.hash_size]
+ %% client_write_key[CipherSpec.key_material]
+ %% server_write_key[CipherSpec.key_material]
+ %% client_write_IV[CipherSpec.IV_size] /* non-export ciphers */
+ %% server_write_IV[CipherSpec.IV_size] /* non-export ciphers */
+ <<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);
+
+ %% 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}.
+
+suites() ->
+ [
+ %% TODO: uncomment when supported
+ %% ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ %% ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ %% TODO: Funkar inte, borde: ?TLS_RSA_WITH_AES_256_CBC_SHA,
+ %% ?TLS_DHE_RSA_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_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_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
+ ].
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+hash(?MD5, Data) ->
+ crypto:md5(Data);
+hash(?SHA, Data) ->
+ crypto:sha(Data).
+
+hash_update(?MD5, Context, Data) ->
+ crypto:md5_update(Context, Data);
+hash_update(?SHA, Context, Data) ->
+ crypto:sha_update(Context, Data).
+
+hash_final(?MD5, Context) ->
+ crypto:md5_final(Context);
+hash_final(?SHA, Context) ->
+ crypto:sha_final(Context).
+
+%%pad_1(?NULL) ->
+%% "";
+pad_1(?MD5) ->
+ <<"666666666666666666666666666666666666666666666666">>;
+pad_1(?SHA) ->
+ <<"6666666666666666666666666666666666666666">>.
+
+%%pad_2(?NULL) ->
+%% "";
+pad_2(?MD5) ->
+ <<"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"
+ "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\">>;
+pad_2(?SHA) ->
+ <<"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"
+ "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\">>.
+
+mac_hash(?NULL, _Secret, _Data) ->
+ <<>>;
+mac_hash(Method, Secret, Data) ->
+ InnerHash = hash(Method, [Secret, pad_1(Method), Data]),
+ hash(Method, [Secret, pad_2(Method), InnerHash]).
+
+handshake_hash(Method, HandshakeHash, Extra) ->
+ HSH = hash_update(Method, HandshakeHash, Extra),
+ hash_final(Method, HSH).
+
+handshake_hash(Method, MasterSecret, undefined, HandshakeHash) ->
+ InnerHash =
+ handshake_hash(Method, HandshakeHash,
+ [MasterSecret, pad_1(Method)]),
+ hash(Method, [MasterSecret, pad_2(Method), InnerHash]);
+handshake_hash(Method, MasterSecret, Sender, HandshakeHash) ->
+ InnerHash =
+ handshake_hash(Method, HandshakeHash,
+ [Sender, MasterSecret, pad_1(Method)]),
+ hash(Method, [MasterSecret, pad_2(Method), InnerHash]).
+
+get_sender(client) -> "CLNT";
+get_sender(server) -> "SRVR";
+get_sender(none) -> "".
+
+generate_keyblock(MasterSecret, ServerRandom, ClientRandom, WantedLength) ->
+ gen(MasterSecret, [MasterSecret, ServerRandom, ClientRandom],
+ WantedLength, 0, $A, 1, []).
+
+gen(_Secret, _All, Wanted, Len, _C, _N, Acc) when Wanted =< Len ->
+ <<Block:Wanted/binary, _/binary>> = list_to_binary(lists:reverse(Acc)),
+ Block;
+gen(Secret, All, Wanted, Len, C, N, Acc) ->
+ Prefix = lists:duplicate(N, C),
+ SHA = crypto:sha([Prefix, All]),
+ MD5 = crypto:md5([Secret, SHA]),
+ gen(Secret, All, Wanted, Len + 16, C+1, N+1, [MD5 | Acc]).
diff --git a/lib/ssl/src/ssl_sup.erl b/lib/ssl/src/ssl_sup.erl
new file mode 100644
index 0000000000..bd5a02417a
--- /dev/null
+++ b/lib/ssl/src/ssl_sup.erl
@@ -0,0 +1,100 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-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_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callback
+-export([init/1]).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+%%%=========================================================================
+%%% Supervisor callback
+%%%=========================================================================
+%% init([]) -> {ok, {SupFlags, [ChildSpec]}}
+%%
+init([]) ->
+
+ %% OLD ssl - moved start to ssl.erl only if old
+ %% ssl is acctualy run!
+ %%Child1 = {ssl_server, {ssl_server, start_link, []},
+ %% permanent, 2000, worker, [ssl_server]},
+
+ %% Does not start any port programs so it does matter
+ %% so much if it is not used!
+ Child2 = {ssl_broker_sup, {ssl_broker_sup, start_link, []},
+ permanent, 2000, supervisor, [ssl_broker_sup]},
+
+
+ %% New ssl
+ SessionCertManager = session_and_cert_manager_child_spec(),
+ ConnetionManager = connection_manager_child_spec(),
+
+ {ok, {{one_for_all, 10, 3600}, [Child2, SessionCertManager,
+ ConnetionManager]}}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+session_and_cert_manager_child_spec() ->
+ Opts = manager_opts(),
+ Name = ssl_manager,
+ StartFunc = {ssl_manager, start_link, Opts},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [ssl_manager],
+ Type = worker,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+connection_manager_child_spec() ->
+ Name = ssl_connection,
+ StartFunc = {ssl_connection_sup, start_link, []},
+ Restart = permanent,
+ Shutdown = 4000,
+ Modules = [ssl_connection],
+ Type = supervisor,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+
+manager_opts() ->
+ CbOpts = case application:get_env(ssl, session_cb) of
+ {ok, Cb} when is_atom(Cb) ->
+ [{session_cb, Cb}];
+ _ ->
+ []
+ end,
+ case application:get_env(ssl, session_lifetime) of
+ {ok, Time} when is_integer(Time) ->
+ [{session_lifetime, Time}| CbOpts];
+ _ ->
+ CbOpts
+ end.
+
diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl
new file mode 100644
index 0000000000..e0013c48ac
--- /dev/null
+++ b/lib/ssl/src/ssl_tls1.erl
@@ -0,0 +1,251 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-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: Handles tls1 encryption.
+%%----------------------------------------------------------------------
+
+-module(ssl_tls1).
+
+-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]).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
+master_secret(PreMasterSecret, ClientRandom, ServerRandom) ->
+ %% RFC 2246 & 4346 - 8.1 %% master_secret = PRF(pre_master_secret,
+ %% "master secret", ClientHello.random +
+ %% ServerHello.random)[0..47];
+ prf(PreMasterSecret, <<"master secret">>,
+ [ClientRandom, ServerRandom], 48).
+
+finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
+ %% RFC 2246 & 4346 - 7.4.9. Finished
+ %% struct {
+ %% opaque verify_data[12];
+ %% } Finished;
+ %%
+ %% verify_data
+ %% PRF(master_secret, finished_label, MD5(handshake_messages) +
+ %% SHA-1(handshake_messages)) [0..11];
+ MD5 = hash_final(?MD5, MD5Hash),
+ SHA = hash_final(?SHA, SHAHash),
+ prf(MasterSecret, finished_label(Role), [MD5, SHA], 12).
+
+
+certificate_verify(Algorithm, {MD5Hash, SHAHash}) when Algorithm == rsa;
+ Algorithm == dh_rsa;
+ Algorithm == dhe_rsa ->
+ 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 ->
+ hash_final(?SHA, SHAHash).
+
+setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize,
+ KeyMatLen, IVSize) ->
+ %% RFC 2246 - 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]
+ %% client_write_IV[SecurityParameters.IV_size]
+ %% server_write_IV[SecurityParameters.IV_size]
+ WantedLength = 2 * (HashSize + KeyMatLen + IVSize),
+ KeyBlock = prf(MasterSecret, "key expansion",
+ [ServerRandom, ClientRandom], WantedLength),
+ <<ClientWriteMacSecret:HashSize/binary,
+ ServerWriteMacSecret:HashSize/binary,
+ ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary,
+ ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock,
+ {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}.
+
+mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
+ Length, Fragment) ->
+ %% RFC 2246 & 4346 - 6.2.3.1.
+ %% HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type +
+ %% TLSCompressed.version + TLSCompressed.length +
+ %% TLSCompressed.fragment));
+ 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.
+
+suites() ->
+ [
+ %% TODO: uncomment when supported
+ %% ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ %% ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ %% TODO: Funkar inte, borde: ?TLS_RSA_WITH_AES_256_CBC_SHA,
+ %% ?TLS_DHE_RSA_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_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_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_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
+ ].
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+%%%% HMAC and the Pseudorandom Functions RFC 2246 & 4346 - 5.%%%%
+hmac_hash(?NULL, _, _) ->
+ <<>>;
+hmac_hash(?MD5, Key, Value) ->
+ crypto:md5_mac(Key, Value);
+hmac_hash(?SHA, Key, Value) ->
+ crypto:sha_mac(Key, Value).
+
+% First, we define a data expansion function, P_hash(secret, data) that
+% uses a single hash function to expand a secret and seed into an
+% arbitrary quantity of output:
+%% P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
+%% HMAC_hash(secret, A(2) + seed) +
+%% HMAC_hash(secret, A(3) + seed) + ...
+
+p_hash(Secret, Seed, WantedLength, Method) ->
+ p_hash(Secret, Seed, WantedLength, Method, 0, []).
+
+p_hash(_Secret, _Seed, WantedLength, _Method, _N, [])
+ when WantedLength =< 0 ->
+ [];
+p_hash(_Secret, _Seed, WantedLength, _Method, _N, [Last | Acc])
+ when WantedLength =< 0 ->
+ Keep = byte_size(Last) + WantedLength,
+ <<B:Keep/binary, _/binary>> = Last,
+ lists:reverse(Acc, [B]);
+p_hash(Secret, Seed, WantedLength, Method, N, Acc) ->
+ N1 = N+1,
+ Bin = hmac_hash(Method, Secret, [a(N1, Secret, Seed, Method), Seed]),
+ p_hash(Secret, Seed, WantedLength - byte_size(Bin), Method, N1, [Bin|Acc]).
+
+
+%% ... Where A(0) = seed
+%% A(i) = HMAC_hash(secret, A(i-1))
+%% a(0, _Secret, Seed, _Method) ->
+%% Seed.
+%% a(N, Secret, Seed, Method) ->
+%% hmac_hash(Method, Secret, a(N-1, Secret, Seed, Method)).
+a(0, _Secret, Seed, _Method) ->
+ Seed;
+a(N, Secret, Seed0, Method) ->
+ Seed = hmac_hash(Method, Secret, Seed0),
+ a(N-1, Secret, Seed, Method).
+
+split_secret(BinSecret) ->
+ %% L_S = length in bytes of secret;
+ %% L_S1 = L_S2 = ceil(L_S / 2);
+ %% The secret is partitioned into two halves (with the possibility of
+ %% one shared byte) as described above, S1 taking the first L_S1 bytes,
+ %% and S2 the last L_S2 bytes.
+ Length = byte_size(BinSecret),
+ Div = Length div 2,
+ EvenLength = Length - Div,
+ <<Secret1:EvenLength/binary, _/binary>> = BinSecret,
+ <<_:Div/binary, Secret2:EvenLength/binary>> = BinSecret,
+ {Secret1, Secret2}.
+
+prf(Secret, Label, Seed, WantedLength) ->
+ %% PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
+ %% P_SHA-1(S2, label + seed);
+ {S1, S2} = split_secret(Secret),
+ LS = list_to_binary([Label, Seed]),
+ crypto:exor(p_hash(S1, LS, WantedLength, ?MD5),
+ p_hash(S2, LS, WantedLength, ?SHA)).
+
+%%%% Misc help functions %%%%
+
+finished_label(client) ->
+ <<"client finished">>;
+finished_label(server) ->
+ <<"server finished">>.
+
+hash_final(?MD5, Conntext) ->
+ crypto:md5_final(Conntext);
+hash_final(?SHA, Conntext) ->
+ crypto:sha_final(Conntext).
+
+
+
+
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
new file mode 100644
index 0000000000..603c419653
--- /dev/null
+++ b/lib/ssl/vsn.mk
@@ -0,0 +1,42 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-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%
+#
+
+SSL_VSN = 3.10.7
+
+TICKETS = 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