This manual describes the old interface to Secure Socket Layer. It should not be used for new development.
The information in this manual is not up-to-date, and will not be
updated in the future. However, the following applies for the
SSL 2.0 version: Windows and UNIX are supported; the "-log " option
in SSLFlags
is not supported anymore.
SSL Sockets are the secure BSD UNIX interface to communication protocols based on SSLeay library written by Eric Young (eay@mincom.oz.au).
Users of the SSL sockets must be aware of the patent rights and export restrictions of cryprographic algorithms in Europe and USA. Please see the Requirements section and the SSLeay documentations on the legal aspects on algorithm use.
Only the AF_INET
protocol family and the STREAM
protocols are supported.
A socket is a full duplex communications channel between two UNIX processes, either over a network to a remote machine, or locally between processes running on the same machine. A socket connects two parties, the initiator and the connector. The initiator is the UNIX process which first opens the socket. It issues a series of system calls to set up the socket and then waits for another process to create a connection to the socket. When the connector starts, it also issues a series of system calls to set up the socket. Then both processes continue running and the communications channel is bound to a file descriptor which both processes use for reading and writing.
listen(Protocol, Family, Address, Mode)
Sets up a socket listening to Address
. It also binds
the name specified by Address
to the socket.
Protocol
must be the atom STREAM
(connection-oriented). Family
must be
AF_INET
.
The UNIX process that is to connect to the socket can run on
any other accessible machine on the Internet. The
Address
is an integer specifying what port number is
to be listened to. This port number uniquely identifies the
socket on the machine. If port number 0
is chosen, a
free port number is automatically chosen by the UNIX
kernel. Note:
These port numbers are not to be
confused with Erlang ports; they are UNIX-socket ports.
Socket ports are used with a host name to create an end
point for a socket connection. listen/4
with
Protocol
=STREAM
returns the tuple
{Filedescriptor, Portnumber}
. Filedescriptor
is an integer specifying the file descriptor assigned to the
socket which is being listened to. Portnumber
is an
integer specifying the port number assigned to the socket.
If Address
is not zero in the call to listen
,
the returned port number is equal to Address
.
Mode
must be one of:
{packet, N} {binary_packet, N} raw == {packet, 0} onebyte == {packet, 1} twobytes == {packet, 2} fourbytes == {packet, 4} asn1
where valid values for N
are 0, 1, 2
and
4
. This parameter specifies the way to read or write
to the socket. If Mode
is {packet, N}
, then
each series of bytes written to the socket will be prepended
with N
bytes indicating the length of the
string. These N
bytes are in binary format, with the
most significant byte first. In this way it can be checked
that all bytes that were written also are read. For this
reason no partitioned messages will ever be delivered.
If Mode
is {binary_packet, N}
, the socket is
in binary mode, and binary data will be prepended with a
bytes header of N
. When data is delivered to a
socket in binary mode, the data will be delivered as a
binary (instead of being unpacked as a byte list.) If
N
is 0, nothing will be prepended. If Mode
is
asn1
, the receiving side of the connection will
assume that BER-coded ASN.1
messages are sent on the
socket. The header of the ASN.1
message will then be
checked to find out the total length of the ASN.1
message. That number of bytes will then be read from the
socket and only one message at a time delivered to the
Erlang runtime system.
Note! the asn1
mode will only
work if all BER encoded data uses the definite length form.
If the indefinite length form is used (the sender's decision),
only the tag and length bytes will be received and then the
connection will be broken. If the indefinite length form can
occur (received by the Erlang runtime system) the raw
or
{packet,0}
mode should be used.
For this reason if the options {packet, N}
,
{binary_packet, N}
(N > 0) or asn1
are set on
the socket, all that is written at the sender side will be
read (in one chunk) on the reader side. This can be very
convenient as this is not guaranteed in TCP. In TCP the
messages may be divided partition in unpredictable
ways. With TCP a STREAM of bytes is delivered; it is not a
datagram protocol.
Example:
ListenSocket = ssl_socket:listen('STREAM', 'AF_INET', 3000, {packet, 2}).
ListenSocket
may be bound to {3, 3000}, where 3 is a
file descriptor and 3000 is the port listened to. If not
successful the process evaluating listen
evaluates
exit({listen, syncerror})
. This happens if, for
example, Portnumber
is set to a number which is
already occupied on the machine.
accept(ListenSocket, SSLFlags)
After a listen
, the incoming requests to connect for
a connection oriented (STREAM) socket may be accepted. This
is done with the call accept
. The parameter
ListenSocket
is the tuple returned from the previous
call to listen
. The call to accept
suspends
the caller until a connection has been established from
outside. A process identifier is returned to the caller.
This process is located between the user and the actual
socket. All communication with the socket is through this
process, which understands a series of messages and also
sends a series of messages to the process that initiated the
call to accept
.
SSLFlags
is an ASCII list which contains a
combination of the following options separated by space/s:
-cert
ARG specify the certificate file to use. File
should be in PEM format. Server must always have a
certificate.
-key
ARG specify the private key file to use. File
should be in PEM format. If certificate file contains
private key then there is no need to specify private key
file.
-cipher
ARG specify the list of ciphers to use, list
of the following: NULL-MD5 RC4-MD5 EXP-RC4-MD5 IDEA-CBC-MD5
RC2-CBC-MD5 EXP-RC2-CBC-MD DES-CBC-MD5 DES-CBC-SHA
DES-CBC3-MD5 DES-CBC3-SHA DES-CFB-M1, separated by ':'. If
this option is not specified then the value of environment
variable SSL_CIPHER will be used.
-verify
ARG specify the certificate verification
level. ARG could be one of: 0 - server does not ask for a
client certificate; client does not check the server
certificate but uses it for establishing a SSL connection 1
- server asks for client certificate; both do a certificate
check; if it fails because of unknown issuer certificate the
connection still gets established 2 - server asks for client
certificate; both do a certificate check; SSL connection
gets established only if the certificate check is
successful. Note:
default level of verification is
0.
-log
ARG specify the log file
Example:
Socket = ssl_socket:accept(ListenSocket, "-cert server_cert.pem -key server_key.pem")
After the statement above it is possible to communicate with the socket. The messages, which may be sent to the socket are:
Socket ! {self(), {deliver, ByteList}}.
or
Socket ! {self(), {deliver, Binary}}.
Causes Binary
/ByteList
to be written to the
socket.
Socket ! {self(), close}.
Closes the socket down in an orderly way. If the socket is not closed in this way, it will be automatically closed when the process terminates. The messages that can be received from the socket are best explained by an example:
receive {Socket, {socket_closed, normal}} -> ok; %% socket closed by foreign host {Socket, {socket_closed, Error}} -> notok; %% something has happened to the socket {Socket, {fromsocket, Bytes}} -> {bytes, Bytes} end.
Two messages may be sent to the socket, i.e. deliver and close. The socket can send three messages back: two error messages and one message indicating the arrival of new data. All of these are shown below.
Input to the socket:
- {self(), {deliver, ByteList}} - {self(), {deliver, Binary}} - {self(), close}
Output from the socket:
- {Socket, {socket_closed, normal}} - {Socket, {socket_closed, Error}} - {Socket, {fromsocket, ByteList}} - {Socket, {fromsocket, Binary}}
It may sometimes be convenient to listen to several sockets at the same time. This is most easily achieved by having one Erlang process for each port number for listening.
Another common situation in network programming is when a server is listening to one or more ports waiting for a connect message from the network. Once it arrives, a separate process is spawned to specifically handle the connection. It returns and continues waiting for new connections from the network.
The code for this could be similar to the following:
top(Port) -> Listen = ssl_socket:listen('STREAM', 'AF_INET', Port, {packet, 2}), loop(Listen). loop(Listen) -> Pid = spawn(mymod, connection, [Listen, self()]), receive {Pid, ok} -> loop(Listen) end. connection(Listen, Father) -> Socket = ssl_socket:accept(Listen, "-cert ssl_server.pem"), Father ! {self(), ok}, Socket ! {self(), {deliver, "Hello there"}}, ..... ....
This code first spawns a process, and lets the new process
be suspended while waiting for the connection from the
network. Once the new process is connected, the original
process is informed about it by the {self(), ok}
message. That process then spawns another, etc.
If there is a listening function to a port and
accept/2
has been evaluated, the process is suspended
and cannot be aborted. In order to stop accepting input, the
process making the call receives an EXIT signal. The accept
call will then terminate and no more connections will be
accepted until a new accept call is made to the same
ListenSocket
. To achieve this, loop(Listen)
can be modified in the following way:
loop(Listen) -> Pid = spawn(mymod, connection, [Listen, self()]), loop(Pid, Listen). loop(Pid, Listen) -> receive {Pid, ok} -> loop(Listen); stop -> exit(Pid, abort), exit(normal) end.
After the code above has received the stop
message
and exited, there is no error in the Listen
socket. It is still intact and can be used again in a new
call to loop/1
.
Another common situation in socket programming is wanting to listen to an address for connections, and then having all the connections handled by a single special process (that reads and writes several sockets simultaneously). The code for that would be similar to the following example:
my_accept(ListenFd, User) -> S = ssl_socket:accept(ListenFd, "-cert ssl_server.pem"), ssl_socket:controlling_process(S, User), my_accept(ListenFd, User).
The process User runs code that is similar to the following:
run(Sockets) when list(Sockets) -> receive {From, {fromsocket, Bytes}} -> case lists:member(From, Sockets) of true -> %% old socket handle_input(Bytes), run(Sockets); false -> %% new connection handle_input(Bytes), run([From|Sockets]) end; .......... etc.
client(Protocol, Family, Address, Mode, SSLFlags)
If another UNIX process is already listening to a socket,
the socket on the client side may be opened with this
call. As before, Protocol
must be the atom
STREAM
and Family
must be AF_INET
.
Address
must be a tuple of the type {IPAddress,
Portnumber}
. It may be argued that users should not have
to know port numbers, only names of services as in the BSD
library routine getservbyname()
. However, this idea
has not been implemented in this package, so when a client
is to be connected to a socket over the Internet, the port
number has to be specified. Examples:
Socket1 = ssl_socket:client('STREAM', 'AF_INET', {'gin.eua.ericsson.se', 1000}, raw, "-cert client_cert.pem -cert client_key.pem"), Socket2 = ssl_socket:client('STREAM', 'AF_INET', {'134.138.99.53', 1002}, asn1, "-cert ssl_client.pem"), Socket3 = ssl_socket:client('STREAM', 'AF_INET', {'gin', 1003}, {binary_packet, 4}, ""),
As can be seen in the examples above, several formats are
allowed for Address
. The Mode
variable in the
call to client
is the same as in the calls to
listen
. The SSLFlags
variable is the same as
in the calls to accept
, with one exception it is
recommended for client to have a certificate but it is not
necessary.
client
returns a process identifier of a process with
the same characteristics as the process described for the
accept
call above.
controlling_process(Socket, Pid)
When a value has been returned from the call to
accept
or the call to client
, the Pid of the
process which performed the initiation is known by the
socket. All output from the socket is sent to this
process. All input to the socket must also be wrapped with
the Pid of the original process.
If the controlling process is to be changed, the socket must be informed. This is similar to the way an Erlang port needs to know the Pid of the process which opened it. The socket (and the port) must know where to send messages. The function above assigns a new controlling process to the socket. Thus, this function ensures that all output from the socket is sent to a process other than the process which created the socket. It also ensures that no messages from the socket are lost while the switch takes place.
Returns the name of the peer to Socket
.
If AF_UNIX is used peername
returns the filename
used as address of a string.
If AF_INET is used peername
returns the tuple
{Portnumber, IPAddress}
.
Returns the official name of the current host.
Returns the official name of the host with the address
IPAddress
.
Closes the socket. This is equivalent to sending a
{self(), close}
message to the process controlling
the socket. It also operates on sockets returned by the
listen
call. This is the method to stop the
listening to a socket.
Starts the socket server.
Stops the socket server, and closes all open sockets.
Even if a socket is opened in {packet, N}
mode, it is
possible to write binaries to it. The receiving part of the
socket determines if data from the socket is to be unpacked as a
byte list or not. i.e. a sender may be in binary mode
({binary_packet, N}
) and the receiver in byte list mode
({packet, N}
) or vice versa. The only restriction is
that the packet sizes must match.
The modes raw
and twobytes
are kept for backwards
compatibility, and the modes onebyte
and fourbytes
have been added for forward compatibility.
In order to be able to use this module it is required to generate a key and a certificate.
For test purposes a private key and a certificate can be generated by using:
req -new -x509 -nodes -out test.pem -keyout test.pem ln -sf test.pem `x509 -noout -hash < test.pem`.0
Certificate signing request can be generated by using:
req -new -out csr.pem -keyout key.pem -days XXX
A certificate signing request (csr.pem) is then could be send to a Certificate Authority (CA) for the purpose of of CA signing the request.
Some of Certification Authorities:
http://www.verisign.com - Verisign http://www.thawte.com/certs - Thawte Consulting http://www.eurosign.com - EuroSign http://www.cost.se - COST
Environment variables SSL_CERT_DIR
and
SSL_CERT_FILE
could be used to set the location of the
certificate of the trusted certifying authority. This is used
during the certificate verification process.
When using this module, both client and server must be SSL-enabled. A SSL-server will hang if a non-SSL client tries to connect to it. If a SSL-client tries to connect to a non-SSL-server, the connection will fail.
SSL sockets need the SSLeay version 0.6.6 package installed in shared library form. You can get it from ftp://ftp.psy.uq.oz.au/pub/Crypto/SSL or you can find other mirrored locations at http://www.psy.uq.oz.au/~ftp/Crypto/.
The SSLeay package implements several well known cryptographic algorithms. Some of these are protected by software patents in some countries. The package can be configured to exclude algorithms at installation. Below follows a summary on software patents and restrictions for algorithms in SSLeay, see the SSLeay documentation for details:
The use of the RSA algorithm must be licensed in the USA due to US software patents. This includes any products sold to the USA that use the SSLeay RSA package. Export from the USA is restricted for software containing cryptographic algorithms.
The IDEA algorithm is protected by a patent in Europe and must be licensed.
General use of cryptography is prohibited in France.
At this stage it is not possible to establish connection between a server and a client residing on the same Erlang node due to blocking of SSL_connect().
Please note that at this stage it is not possible to use private key encrypted with a pass phrase. To remove pass phrase do:
rsa -in key-protected -out key-unprotected.pem
The result of this restriction is that the secury of the private key relies on the file system security mechanism. Keep the private key and the certificate in separate files.