Next: Certificate Handling, Previous: TLS Authentication Methods, Up: Internal architecture of GnuTLS
As with authentication methods, the TLS extensions handlers can be implemented using the following interface.
Here there are two functions, one for receiving the extension data and one for sending. These functions have to check internally whether they operate in client or server side.
A simple example of an extension handler can be seen in
ext_srp.c
After implementing these functions, together with the
extension number they handle, they have to be registered in
gnutls_extensions.c
in the _gnutls_extensions
structure.
Adding support for a new TLS extension is done from time to time, and
the process to do so is not difficult. Here are the steps you need to
follow if you wish to do this yourself. For sake of discussion, let's
consider adding support for the hypothetical TLS extension
foobar
.
configure
option like --enable-foobar
or --disable-foobar
.
Which to chose depends on whether you intend to make the extension be enabled by default. Look at existing checks (i.e., SRP, authz) for how to model the code. For example:
AC_MSG_CHECKING([whether to disable foobar support]) AC_ARG_ENABLE(foobar, AS_HELP_STRING([--disable-foobar], [disable foobar support]), ac_enable_foobar=no) if test x$ac_enable_foobar != xno; then AC_MSG_RESULT(no) AC_DEFINE(ENABLE_FOOBAR, 1, [enable foobar]) else ac_full=0 AC_MSG_RESULT(yes) fi AM_CONDITIONAL(ENABLE_FOOBAR, test "$ac_enable_foobar" != "no")
These lines should go in lib/m4/hooks.m4
.
extensions_t
in gnutls_int.h
.
A good name for the value would be GNUTLS_EXTENSION_FOOBAR. Check with http://www.iana.org/assignments/tls-extensiontype-values for allocated values. For experiments, you could pick a number but remember that some consider it a bad idea to deploy such modified version since it will lead to interoperability problems in the future when the IANA allocates that number to someone else, or when the foobar protocol is allocated another number.
_gnutls_extensions
in gnutls_extensions.c
.
A typical entry would be:
int ret; /* ... */ #if ENABLE_FOOBAR ret = gnutls_ext_register (GNUTLS_EXTENSION_FOOBAR, "FOOBAR", GNUTLS_EXT_TLS, _gnutls_foobar_recv_params, _gnutls_foobar_send_params); if (ret != GNUTLS_E_SUCCESS) return ret; #endif
The GNUTLS_EXTENSION_FOOBAR is the integer value you added to
gnutls_int.h
earlier. The two functions are new functions that
you will need to implement, most likely you'll need to add an
#include "ext_foobar.h"
as well.
ext_foobar.c
and ext_foobar.h
that implements the extension.
The functions you are responsible to add are those mentioned in the previous step. As a starter, you could add this:
int _gnutls_foobar_recv_params (gnutls_session_t session, const opaque * data, size_t data_size) { return 0; } int _gnutls_foobar_send_params (gnutls_session_t session, opaque * data, size_t _data_size) { return 0; }
The _gnutls_foobar_recv_params
function is responsible for
parsing incoming extension data (both in the client and server).
The _gnutls_foobar_send_params
function is responsible for
sending extension data (both in the client and server).
If you receive length fields that doesn't match, return
GNUTLS_E_UNEXPECTED_PACKET_LENGTH
. If you receive invalid
data, return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER
. You can use
other error codes too. Return 0 on success.
The function typically store some information in the session
variable for later usage. If you need to add new fields there, check
tls_ext_st
in gnutls_int.h
and compare with existing TLS
extension specific variables.
Recall that both the client and server both send and receives parameters, and your code most likely will need to do different things depending on which mode it is in. It may be useful to make this distinction explicit in the code. Thus, for example, a better template than above would be:
int _gnutls_foobar_recv_params (gnutls_session_t session, const opaque * data, size_t data_size) { if (session->security_parameters.entity == GNUTLS_CLIENT) return foobar_recv_client (session, data, data_size); else return foobar_recv_server (session, data, data_size); } int _gnutls_foobar_send_params (gnutls_session_t session, opaque * data, size_t data_size) { if (session->security_parameters.entity == GNUTLS_CLIENT) return foobar_send_client (session, data, data_size); else return foobar_send_server (session, data, data_size); }
The functions used would be declared as static
functions, of
the appropriate prototype, in the same file.
When adding the files, you'll need to add them to Makefile.am
as well, for example:
if ENABLE_FOOBAR COBJECTS += ext_foobar.c HFILES += ext_foobar.h endif
Normally the client will have one API to request use of the extension, and setting some extension specific data. The server will have one API to let the library know that it is willing to accept the extension, often this is implemented through a callback but it doesn't have to.
The APIs need to be added to includes/gnutls/gnutls.h
or
includes/gnutls/extra.h
as appropriate. It is recommended that
if you don't have a requirement to use the LGPLv2.1+ license for your
extension, that you place your work under the GPLv3+ license and thus
in the libgnutls-extra library.
You can implement the API function in the ext_foobar.c
file, or
if that file ends up becoming rather larger, add a
gnutls_foobar.c
file.
To make the API available in the shared library you need to add the
symbol in lib/libgnutls.map
or
libextra/libgnutls-extra.map
as appropriate, so that the symbol
is exported properly.
When writing GTK-DOC style documentation for your new APIs, don't
forget to add Since:
tags to indicate the GnuTLS version the
API was introduced in.