Do you need help on a specific subject? Use the contact form (Request a blog entry) on the right hand side.


Swift and OpenSSL, part 4: Client details

In the previous post we looked at the big picture of how a SSL connection is created from a client to a SSL-Server.

In this post I want to take a look at some of the details involved.

The first detail is an answer to the question: What is a "ssl-session"?

SSL Session

I am sure that the people that work on openSSL can give a very good answer. But for us API users we can take comfort in the fact that we don't need to know.

That is, an SSL session represents a connection between a SSL-client and a SSL-server. But other than that, we don't need to know how it is represented.

The openSSL team has made this very clear: All sessions are referred to by an OpaquePointer.

We create a session by calling SSL_new and get back an OpaquePointer. All use of the internal structure that hides behind the pointer can be done though API calls.

Usually those API calls take multiple parameters of which the first is the OpaquePointer of the session we want to use.

For example the openSSL read. It is defined as:

int SSL_read(SSL *ssl, void *buf, int num);
Which appears in Swift as:

SSL_read(ssl: OpaquePointer!, buf: UnsafeMutableRawPointer!, num: Int32) -> Int32

The first parameter is the OpaquePointer to the session. The second parameter a pointer to the buffer in which the data should be written and the third parameter is the size of the buffer. Just like the POSIX read call it returns an integer that contains either the number of bytes read, or an error indicator.

The OpaquePointer approach is typical for openSSL 1.1.0. It is used everywhere to avoid exposing the internals of the data structures.

As far as I see, all OpaquePointer's are force unwrapped. This means that they can be nil. In fact, using nil is rather common in C and (to my knowledge) none of the openSSL operations will crash if we pass nil.

Again I glossed over something: how do we create a session?

Well, we use SSL_new ... right, but what parameters do we need?

That brings us to point 2,

SSL Context

To create a session (client side) we provide a context.

SSL *SSL_new(SSL_CTX *ctx);
Which appears in Swift as:

SSL_new(ctx: OpaquePointer!) -> OpaquePointer!

Again we see the usage of OpaquePointer to hide the internal details. Which is good, because now we now we don't need to know :-)

Still we do need to know what a context is used for.

A context is used to store data that a session needs to operate. All kinds of data, most noticeably the certificates! (or at least the path to the certificates).

So before we create the session, we need to set up the context in which that session will operate.

It is worth mentioning that the context used to create a session is copied by that session. Thus if we need to change things afterwards, we need to do that on the session, and not on the context.

This allows us to store a context and keep it handy whenever we need to create a new session.

I strongly suggest you lookup what data can be set in the context, or alternatively know that you can set all parameters necessary for a session in the original context - so you know where to look.

Oh, about those OpaquePointer's...


They hide their structure, but those structures take up memory. And memory must be allocated and deallocated.

You'll see where this is going... yup, reference counters. OpenSSL has them.

When a new session (or context or ... whatever) is created the ref-count is set to 1. When we are done with it, we need to decrease the ref-count. This is done by "free" calls. Each internal openSSL structure has an associated "free" call to decrement the ref-count. Once the ref-count reaches zero, the memory is deallocated.

The SSL_new has a corresponding SSL_free, the SSL_CTX_new has a SSL_CTX_free etc.

When data is retrieved (copied) from another data structure, sometimes openSSL will allocate a new structure for us. And sometimes it won't. It is always a good idea to check the documentation. But there are a couple of calls where this has been made explicit in the name of the call.

These can be recognized by their xxx_get0_xxx and xxx_get1_xxx signature.

The 'get0' returns a structure that should NOT be freed. While the 'get1' returns a structure that MUST be freed.

Now the last of the details that I glossed over, but do want to mention

Certificate Verification

To verify if a client connection to a server is secure, it first should be checked that the server presented a certificate. If that did not happen, there can be no secure connection. OpenSSL does not implicitly check this, we need to do that ourselves.

And then of course the results of the verification must be checked. If that fails, that does not necessary mean that the connection should not be established.

As an example: If we browse to a website that uses self-signed certificated, the verification will fail, but we can still decide to accept that certificate.

In the same way, when a certificate verification fails, it is often a good idea to ask the user what he wants. Does he want to continue or not?

Next in the series: a quick look at the server side.

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

No comments:

Post a Comment