diff options
author | Erlang/OTP <otp@erlang.org> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <otp@erlang.org> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/ssl | |
download | erlang-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/ssl')
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ö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ö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 (eay@cryptsoft.com). This product includes software written by Tim Hudson (tjh@cryptsoft.com). Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com). 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ö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 + * openssl-core@openssl.org. + * + * 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 + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ </code> + </section> + + <section> + <title>SSLeay License</title> + <code type="none"> +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * 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 (tjh@cryptsoft.com). + * + * 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 (eay@cryptsoft.com)" + * 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 (tjh@cryptsoft.com)" + * + * 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> CallbackModule = + atom()</c> + </p> <p><c> DataTag = + atom() - tag used in socket data message.</c></p> + <p><c> ClosedTag = atom() - tag used in + socket close message.</c></p> + + <p><c>verify_type() = verify_none | verify_peer</c></p> + + <p><c>path() = string() - representing a file path.</c></p> + + <p><c>host() = hostname() | ipaddress()</c></p> + + <p><c>hostname() = string()</c></p> + + <p><c> + ip_address() = {N1,N2,N3,N4} % IPv4 + | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6 </c></p> + + <p><c>sslsocket() - opaque to the user. </c></p> + + <p><c>protocol() = sslv3 | tlsv1 </c></p> + + <p><c>ciphers() = [ciphersuite()] | sting() (according to old API)</c></p> + + <p><c>ciphersuite() = + {key_exchange(), cipher(), hash(), exportable()}</c></p> + + <p><c>key_exchange() = rsa | dh_dss | dh_rsa | dh_anon | dhe_dss + | dhe_rsa | krb5 | KeyExchange_export + </c></p> + + <p><c>cipher() = rc4_128 | idea_cbc | des_cbc | '3des_ede_cbc' + des40_cbc | dh_dss | aes_128_cbc | aes_256_cbc | + rc2_cbc_40 | rc4_40 </c></p> + + <p> <c>hash() = md5 | sha + </c></p> + + <p> <c>exportable() = export | no_export | ignore + </c></p> + + <p><c>ssl_imp() = new | old - default is old.</c></p> + + </section> + +<section> + <title>SSL OPTION DESCRIPTIONS</title> + + <taglist> + <tag>{verify, verify_type()}</tag> + <item> If <c>verify_none</c> is specified x509-certificate + path validation errors at the client side + will not automatically cause the connection to fail, as + it will if the verify type is <c>verify_peer</c>. See also + the option verify_fun. + Servers only do the path validation if <c>verify_peer</c> is set to + true, as it then will + send a certificate request to + the client (this message is not sent if the verify option is + <c>verify_none</c>) and you may then also want to specify + the option <c>fail_if_no_peer_cert</c>. + </item> + + <tag>{fail_if_no_peer_cert, boolean()}</tag> + <item>Used together with {verify, verify_peer} by a ssl server. + If set to true, + the server will fail if the client does not have a certificate + to send, e.i sends a empty certificate, if set to false it will + only fail if the client sends a invalid certificate (an empty + certificate is considered valid). + </item> + + <tag>{verify_fun, fun(ErrorList) -> boolean()}</tag> + <item>Used by the ssl client to determine if + x509-certificate path validations errors are acceptable or + if the connection should fail. Defaults to: + +<code> +fun(ErrorList) -> + case lists:foldl(fun({bad_cert,unknown_ca}, Acc) -> + Acc; + (Other, Acc) -> + [Other | Acc] + end, [], ErrorList) of + [] -> + true; + [_|_] -> + false + end +end +</code> + I.e. by default if the only error found was that the CA-certificate + holder was unknown this will be accepted. + + Possible errors in the error list are: + {bad_cert, cert_expired}, {bad_cert, invalid_issuer}, + {bad_cert, invalid_signature}, {bad_cert, name_not_permitted}, + {bad_cert, unknown_ca}, + {bad_cert, cert_expired}, {bad_cert, invalid_issuer}, + {bad_cert, invalid_signature}, {bad_cert, name_not_permitted}, + {bad_cert, cert_revoked} (not implemented yet), + {bad_cert, unknown_critical_extension} or {bad_cert, term()} (Will + be relevant later when an option is added for the user to be able to verify application specific extensions.) + </item> + + <tag>{depth, integer()}</tag> + <item>Specifies the maximum + verification depth, i.e. how far in a chain of certificates the + verification process can proceed before the verification is + considered to fail. Peer certificate = 0, CA certificate = 1, + higher level CA certificate = 2, etc. The value 2 thus means + that a chain can at most contain peer cert, CA cert, next CA + cert, and an additional CA cert. The default value is 1. + </item> + + <tag>{certfile, path()}</tag> + <item>Path to a file containing the + user's certificate. Optional for clients but note + that some servers requires that the client can certify + itself. </item> + <tag>{keyfile, path()}</tag> + <item>Path to file containing user's + private PEM encoded key. As PEM-files may contain several + entries this option defaults to the same file as given by + certfile option.</item> + <tag>{password, string()}</tag> + <item>String containing the user's password. + Only used if the private keyfile is password protected. + </item> + <tag>{cacertfile, path()}</tag> + <item>Path to file containing PEM encoded + CA certificates (trusted certificates used for verifying a peer + certificate). May be omitted if you do not want to verify + the peer.</item> + + <tag>{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> > 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 Binary files differnew file mode 100644 index 0000000000..6fffe30419 --- /dev/null +++ b/lib/ssl/doc/src/note.gif 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ö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ö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ö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 + (eay@cryptsoft.com). + </p> + <p>This product includes software written by Tim Hudson + (tjh@cryptsoft.com). + </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ö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 + (eay@cryptsoft.com). + </p> + <p>This product includes software written by Tim Hudson + (tjh@cryptsoft.com). + </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ö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ögfeldt</prepared> + <responsible>Peter Högfeldt</responsible> + <docno></docno> + <approved>Peter Högfeldt</approved> + <checked></checked> + <date>2003-03-25</date> + <rev>D</rev> + <file>ssl.sgml</file> + </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ögfeldt</prepared> + <responsible>Peter Högfeldt</responsible> + <docno></docno> + <approved>Peter Högfeldt</approved> + <checked>Peter Högfeldt</checked> + <date>2005-03-10</date> + <rev>E</rev> + <file>ssl_app.sgml</file> + </header> + <app>ssl</app> + <appsummary>The SSL Application</appsummary> + <description> + <p>The Secure Socket Layer (SSL) application provides secure + socket communication over TCP/IP. + </p> + </description> + + <section> + <title>Warning</title> + <p>In previous versions of Erlang/OTP SSL it was advised, as a + work-around, to set the operating system environment variable + <c>SSL_CERT_FILE</c> to point at a file containing CA + certificates. That variable is no longer needed, and is not + recognised by Erlang/OTP SSL any more. + </p> + <p>However, the OpenSSL package does interpret that environment + variable. Hence a setting of that variable might have + unpredictable effects on the Erlang/OTP SSL application. It is + therefore adviced to not used that environment variable at all.</p> + </section> + + <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ö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 + (eay@cryptsoft.com). + </p> + <p>This product includes software written by Tim Hudson + (tjh@cryptsoft.com). + </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ö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 Binary files differnew file mode 100644 index 0000000000..96af52360e --- /dev/null +++ b/lib/ssl/doc/src/warning.gif 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 Binary files differnew file mode 100644 index 0000000000..70997bd01f --- /dev/null +++ b/lib/ssl/examples/certs/rnd/RAND 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 = "peter@erix.ericsson.se"}). + +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 |