diff options
author | Simon Josefsson <simon@josefsson.org> | 2005-12-15 10:52:19 +0000 |
---|---|---|
committer | Simon Josefsson <simon@josefsson.org> | 2005-12-15 10:52:19 +0000 |
commit | 3860714e697158a17086fff6a87fa9d22877895a (patch) | |
tree | db6ae00dc003790ce52fc23fb7c100db8498ffeb /libextra | |
parent | 1b6f0a191d7dd2ab25da837494a53992e01b9229 (diff) | |
download | gnutls-3860714e697158a17086fff6a87fa9d22877895a.tar.gz |
Add TLS/IA support.
Diffstat (limited to 'libextra')
-rw-r--r-- | libextra/Makefile.am | 31 | ||||
-rw-r--r-- | libextra/gnutls_ia.c | 916 |
2 files changed, 938 insertions, 9 deletions
diff --git a/libextra/Makefile.am b/libextra/Makefile.am index ca8aa3f745..fc1fa833c0 100644 --- a/libextra/Makefile.am +++ b/libextra/Makefile.am @@ -53,14 +53,13 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = gnutls-extra.pc DISTCLEANFILES = $(pkgconfig_DATA) -EXTRA_DIST = gnutls-extra-api.texi +EXTRA_DIST = gnutls-extra-api.texi ia-api.texi lib_LTLIBRARIES = libgnutls-extra.la -COBJECTS = gnutls_extra.c +libgnutls_extra_la_SOURCES = gnutls_extra.c libgnutls_openssl_la_LDFLAGS = -libgnutls_extra_la_SOURCES = # OpenSSL @@ -95,11 +94,17 @@ libgnutls_extra_la_LDFLAGS += $(LIBOPENCDK_LIBS) endif endif +# TLS/IA + +libgnutls_extra_la_SOURCES += gnutls_ia.c + +# Rest + libgnutls_extra_la_LDFLAGS += \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ $(LZO_LIBS) -libgnutls_extra_la_SOURCES += $(COBJECTS) gnutls_extra.h libgnutls-extra.vers +libgnutls_extra_la_SOURCES += gnutls_extra.h libgnutls-extra.vers libgnutls_extra_la_LIBADD += ../lib/libgnutls.la ../gl/libgnu.la @@ -115,12 +120,20 @@ SUBDIRS += minilzo libgnutls_extra_la_LIBADD += minilzo/libminilzo.la endif -gnutls-extra-api.texi: $(COBJECTS) - @echo "" > gnutls-extra-api.texi - @for i in $(COBJECTS); do \ +gnutls-extra-api.texi: gnutls_extra.c + @echo "" > $@ + @for i in $<; do \ + echo -n "Creating documentation for file $$i... " && \ + ../doc/scripts/gdoc -texinfo $$i >> $@ && \ + echo "ok"; \ + done + +ia-api.texi: gnutls_ia.c + @echo "" > $@ + @for i in $<; do \ echo -n "Creating documentation for file $$i... " && \ - ../doc/scripts/gdoc -texinfo $$i >> gnutls-extra-api.texi && \ + ../doc/scripts/gdoc -texinfo $$i >> $@ && \ echo "ok"; \ done -dist-hook: gnutls-extra-api.texi +dist-hook: gnutls-extra-api.texi ia-api.texi diff --git a/libextra/gnutls_ia.c b/libextra/gnutls_ia.c new file mode 100644 index 0000000000..56677ee772 --- /dev/null +++ b/libextra/gnutls_ia.c @@ -0,0 +1,916 @@ +/* + * Copyright (C) 2005 Free Software Foundation + * + * Author: Simon Josefsson + * + * This file is part of GNUTLS-EXTRA. + * + * GNUTLS-EXTRA is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * GNUTLS-EXTRA is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNUTLS-EXTRA; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "gnutls_int.h" +#include "gnutls_record.h" +#include "gnutls_errors.h" +#include "gnutls_num.h" +#include "gnutls_state.h" + +#define CHECKSUM_SIZE 12 + +struct gnutls_ia_client_credentials_st +{ + gnutls_ia_avp_func avp_func; + void *avp_ptr; +}; + +struct gnutls_ia_server_credentials_st +{ + gnutls_ia_avp_func avp_func; + void *avp_ptr; +}; + +static const char server_finished_label[] = "server phase finished"; +static const char client_finished_label[] = "client phase finished"; +static const char inner_permutation_label[] = "inner secret permutation"; +static const char challenge_label[] = "inner application challenge"; + +/* + * The TLS/IA packet is the InnerApplication token, described as + * follows in draft-funk-tls-inner-application-extension-01.txt: + * + * enum { + * application_payload(0), intermediate_phase_finished(1), + * final_phase_finished(2), (255) + * } InnerApplicationType; + * + * struct { + * InnerApplicationType msg_type; + * uint24 length; + * select (InnerApplicationType) { + * case application_payload: ApplicationPayload; + * case intermediate_phase_finished: IntermediatePhaseFinished; + * case final_phase_finished: FinalPhaseFinished; + * } body; + * } InnerApplication; + * + */ + +/* Send TLS/IA data. If data==NULL && sizeofdata==NULL, then the last + send was interrupted for some reason, and then we try to send it + again. Returns the number of bytes sent, or an error code. If + this return E_AGAIN and E_INTERRUPTED, call this function again + with data==NULL&&sizeofdata=0NULL until it returns successfully. */ +static ssize_t +_gnutls_send_inner_application (gnutls_session_t session, + gnutls_ia_apptype_t msg_type, + const char *data, size_t sizeofdata) +{ + opaque *p = NULL; + size_t plen = 0; + ssize_t len; + + if (data != NULL) + { + plen = sizeofdata + 4; + p = gnutls_malloc (plen); + if (!p) + { + gnutls_assert (); + return GNUTLS_E_MEMORY_ERROR; + } + + *(unsigned char *) p = (unsigned char) (msg_type & 0xFF); + _gnutls_write_uint24 (sizeofdata, p + 1); + memcpy (p + 4, data, sizeofdata); + } + + len = _gnutls_send_int (session, GNUTLS_INNER_APPLICATION, -1, p, plen); + + if (p) + gnutls_free (p); + + return len; +} + +/* Receive TLS/IA data. Store received TLS/IA message type in + *MSG_TYPE, and the data in DATA of max SIZEOFDATA size. Return the + number of bytes read, or an error code. */ +static ssize_t +_gnutls_recv_inner_application (gnutls_session_t session, + gnutls_ia_apptype_t * msg_type, + opaque *data, size_t sizeofdata) +{ + ssize_t len; + opaque pkt[4]; + + len = _gnutls_recv_int (session, GNUTLS_INNER_APPLICATION, -1, pkt, 4); + if (len != 4) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + *msg_type = pkt[0]; + len = _gnutls_read_uint24 (&pkt[1]); + + if (*msg_type != GNUTLS_IA_APPLICATION_PAYLOAD && len != CHECKSUM_SIZE) + { + gnutls_assert (); + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + + if (sizeofdata < len) + { + /* XXX push back pkt to IA buffer? */ + gnutls_assert (); + return GNUTLS_E_SHORT_MEMORY_BUFFER; + } + + if (len > 0) + { + int tmplen = len; + + len = _gnutls_recv_int (session, GNUTLS_INNER_APPLICATION, -1, + data, tmplen); + if (len != tmplen) + { + gnutls_assert (); + /* XXX Correct? */ + return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; + } + } + + return len; +} + +/* Apply the TLS PRF using the TLS/IA inner secret as keying material, + where the seed is the client random concatenated with the server + random concatenated EXTRA of EXTRA_SIZE length (which can be NULL/0 + respectively). LABEL and LABEL_SIZE is used as the label. The + result is placed in pre-allocated OUT of OUTSIZE length. */ +static int +_gnutls_ia_prf (gnutls_session_t session, + size_t label_size, + const char *label, + size_t extra_size, + const char *extra, + size_t outsize, + opaque *out) +{ + int ret; + opaque *seed; + size_t seedsize = 2 * TLS_RANDOM_SIZE + extra_size; + + seed = gnutls_malloc (seedsize); + if (!seed) + { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + memcpy (seed, session->security_parameters.server_random, TLS_RANDOM_SIZE); + memcpy (seed + TLS_RANDOM_SIZE, session->security_parameters.client_random, + TLS_RANDOM_SIZE); + memcpy (seed + 2 * TLS_RANDOM_SIZE, extra, extra_size); + + ret = _gnutls_PRF (session->security_parameters.inner_secret, + TLS_MASTER_SIZE, + label, + label_size, + seed, + seedsize, + outsize, + out); + + gnutls_free (seed); + + return ret; +} + +/** + * gnutls_ia_permute_inner_secret: + * @session: is a #gnutls_session_t structure. + * @session_keys_size: Size of generated session keys (0 if none). + * @session_keys: Generated session keys, used to permute inner secret + * (NULL if none). + * + * Permute the inner secret using the generated session keys. + * + * This can be called in the TLS/IA AVP callback to mix any generated + * session keys with the TLS/IA inner secret. + * + * Return value: Return zero on success, or a negative error code. + **/ +int +gnutls_ia_permute_inner_secret (gnutls_session_t session, + size_t session_keys_size, + const char *session_keys) +{ + return _gnutls_ia_prf (session, + sizeof (inner_permutation_label) - 1, + inner_permutation_label, + session_keys_size, + session_keys, + TLS_RANDOM_SIZE, + session->security_parameters.inner_secret); +} + +/** + * gnutls_ia_generate_challenge: + * @session: is a #gnutls_session_t structure. + * @buffer_size: size of output buffer. + * @buffer: pre-allocated buffer to contain @buffer_size bytes of output. + * + * Generate an application challenge that the client cannot control or + * predict, based on the TLS/IA inner secret. + * + * Return value: Returns 0 on success, or an negative error code. + **/ +int +gnutls_ia_generate_challenge (gnutls_session_t session, + size_t buffer_size, + char *buffer) +{ + return _gnutls_ia_prf (session, + sizeof (challenge_label) - 1, + challenge_label, + 0, + NULL, + buffer_size, + buffer); +} + +/** + * gnutls_ia_extract_inner_secret: + * @session: is a #gnutls_session_t structure. + * @buffer: pre-allocated buffer to hold 48 bytes of inner secret. + * + * Copy the 48 bytes large inner secret into the specified buffer + * + * This function is typically used after the TLS/IA handshake has + * concluded. The TLS/IA inner secret can be used as input to a PRF + * to derive session keys. Do not use the inner secret directly as a + * session key, because for a resumed session that does not include an + * application phase, the inner secret will be identical to the inner + * secret in the original session. It is important to include, for + * example, the client and server randomness when deriving a sesssion + * key from the inner secret. + **/ +void +gnutls_ia_extract_inner_secret (gnutls_session_t session, + char *buffer) +{ + memcpy (buffer, session->security_parameters.inner_secret, TLS_MASTER_SIZE); +} + +/** + * gnutls_ia_endphase_send: + * @session: is a #gnutls_session_t structure. + * @final_p: Set iff this should signal the final phase. + * + * Send a TLS/IA end phase message. + * + * In the client, this should only be used to acknowledge an end phase + * message sent by the server. + * + * In the server, this can be called instead of gnutls_ia_send() if + * the server wishes to end an application phase. + * + * Return value: Return 0 on success, or an error code. + **/ +int +gnutls_ia_endphase_send(gnutls_session_t session, int final_p) +{ + opaque local_checksum[CHECKSUM_SIZE]; + int client = session->security_parameters.entity == GNUTLS_CLIENT; + const char *label = client ? client_finished_label : server_finished_label; + int size_of_label = client ? sizeof (client_finished_label) : + sizeof (server_finished_label); + ssize_t len; + int ret; + + ret = _gnutls_PRF (session->security_parameters.inner_secret, + TLS_MASTER_SIZE, + label, size_of_label - 1, + /* XXX specification unclear on seed. */ + "", 0, CHECKSUM_SIZE, local_checksum); + if (ret < 0) + return ret; + + len = _gnutls_send_inner_application + (session, + final_p ? GNUTLS_IA_FINAL_PHASE_FINISHED : + GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED, + local_checksum, CHECKSUM_SIZE); + + /* XXX Instead of calling this function over and over...? + * while (len == GNUTLS_E_AGAIN || len == GNUTLS_E_INTERRUPTED) + * len = _gnutls_io_write_flush(session); + */ + + if (len < 0) + { + gnutls_assert(); + return len; + } + + return 0; +} + +/** + * gnutls_ia_verify_endphase: + * @session: is a #gnutls_session_t structure. + * @checksum: 12-byte checksum data, received from gnutls_ia_recv(). + * + * Verify TLS/IA end phase checksum data. If verification fails, the + * %GNUTLS_A_INNER_APPLICATION_VERIFICATION alert is sent to the other + * sie. + * + * This function is called when gnutls_ia_recv() return + * %GNUTLS_E_WARNING_IA_IPHF_RECEIVED or + * %GNUTLS_E_WARNING_IA_FPHF_RECEIVED. + * + * Return value: Return 0 on successful verification, or an error + * code. If the checksum verification of the end phase message fails, + * %GNUTLS_E_IA_VERIFY_FAILED is returned. + **/ +int +gnutls_ia_verify_endphase (gnutls_session_t session, char *checksum) +{ + char local_checksum[CHECKSUM_SIZE]; + int client = session->security_parameters.entity == GNUTLS_CLIENT; + const char *label = client ? server_finished_label : client_finished_label; + int size_of_label = client ? sizeof (server_finished_label) : + sizeof (client_finished_label); + int ret; + + ret = _gnutls_PRF (session->security_parameters.inner_secret, + TLS_MASTER_SIZE, + label, size_of_label - 1, + "", 0, CHECKSUM_SIZE, local_checksum); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + if (memcmp (local_checksum, checksum, CHECKSUM_SIZE) != 0) + { + ret = gnutls_alert_send (session, GNUTLS_AL_FATAL, + GNUTLS_A_INNER_APPLICATION_VERIFICATION); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + return GNUTLS_E_IA_VERIFY_FAILED; + } + + return 0; +} + +/** + * gnutls_ia_send: Send peer the specified TLS/IA data. + * @session: is a #gnutls_session_t structure. + * @data: contains the data to send + * @sizeofdata: is the length of the data + * + * Send TLS/IA application payload data. This function has the + * similar semantics with send(). The only difference is that is + * accepts a GNUTLS session, and uses different error codes. + * + * The TLS/IA protocol is synchronous, so you cannot send more than + * one packet at a time. The client always send the first packet. + * + * To finish an application phase in the server, use + * gnutls_ia_endphase_send(). The client cannot end an application + * phase. + * + * If the EINTR is returned by the internal push function (the default + * is send()} then %GNUTLS_E_INTERRUPTED will be returned. If + * %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN is returned, you must call + * this function again, with the same parameters; alternatively you + * could provide a %NULL pointer for data, and 0 for size. + * + * Returns the number of bytes sent, or a negative error code. + **/ +ssize_t +gnutls_ia_send (gnutls_session_t session, char *data, size_t sizeofdata) +{ + ssize_t len; + + len = _gnutls_send_inner_application (session, + GNUTLS_IA_APPLICATION_PAYLOAD, + data, sizeofdata); + + return len; +} + +/** + * gnutls_ia_recv - read data from the TLS/IA protocol + * @session: is a #gnutls_session_t structure. + * @data: the buffer that the data will be read into, must hold >= 12 bytes. + * @sizeofdata: the number of requested bytes, must be >= 12. + * + * Receive TLS/IA data. This function has the similar semantics with + * recv(). The only difference is that is accepts a GNUTLS session, + * and uses different error codes. + * + * If the server attempt to finish an application phase, this function + * will return %GNUTLS_E_WARNING_IA_IPHF_RECEIVED or + * %GNUTLS_E_WARNING_IA_FPHF_RECEIVED. The caller should then invoke + * gnutls_ia_verify_endphase(). + * + * If EINTR is returned by the internal push function (the default is + * @code{recv()}) then GNUTLS_E_INTERRUPTED will be returned. If + * GNUTLS_E_INTERRUPTED or GNUTLS_E_AGAIN is returned, you must call + * this function again, with the same parameters; alternatively you + * could provide a NULL pointer for data, and 0 for size. + * + * Returns the number of bytes received. A negative error code is + * returned in case of an error. The + * %GNUTLS_E_WARNING_IA_IPHF_RECEIVED and + * %GNUTLS_E_WARNING_IA_FPHF_RECEIVED errors are returned when an + * application phase finished message has been sent by the server. + **/ +ssize_t +gnutls_ia_recv (gnutls_session_t session, char *data, size_t sizeofdata) +{ + gnutls_ia_apptype_t msg_type; + ssize_t len; + + len = _gnutls_recv_inner_application (session, &msg_type, + data, sizeofdata); + + if (msg_type == GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED) + return GNUTLS_E_WARNING_IA_IPHF_RECEIVED; + else if (msg_type == GNUTLS_IA_FINAL_PHASE_FINISHED) + return GNUTLS_E_WARNING_IA_FPHF_RECEIVED; + + return len; +} + +/* XXX rewrite the following two functions as state machines, to + handle EAGAIN/EINTERRUPTED? just add more problems to callers, + though. */ + +int +_gnutls_ia_client_handshake (gnutls_session_t session) +{ + char *buf = NULL; + size_t buflen = 0; + char tmp[1024]; /* XXX */ + ssize_t len; + int ret; + const struct gnutls_ia_client_credentials_st * cred = + _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL); + + if (cred == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + while (1) + { + char *avp; + size_t avplen; + + ret = cred->avp_func (session, cred->avp_ptr, + buf, buflen, &avp, &avplen); + if (ret) + { + int tmpret; + tmpret = gnutls_alert_send (session, GNUTLS_AL_FATAL, + GNUTLS_A_INNER_APPLICATION_FAILURE); + if (tmpret < 0) + gnutls_assert (); + return ret; + } + + len = gnutls_ia_send (session, avp, avplen); + gnutls_free (avp); + if (len < 0) + return len; + + len = gnutls_ia_recv (session, tmp, sizeof (tmp)); + if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED || + len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) + { + ret = gnutls_ia_verify_endphase (session, tmp); + if (ret < 0) + return ret; + + ret = gnutls_ia_endphase_send + (session, len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED); + if (ret < 0) + return ret; + } + + if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED) + { + buf = NULL; + buflen = 0; + continue; + } + else if (len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) + break; + + if (len < 0) + return len; + + buflen = len; + buf = tmp; + } + + return 0; +} + +int +_gnutls_ia_server_handshake (gnutls_session_t session) +{ + gnutls_ia_apptype_t msg_type; + ssize_t len; + char buf[1024]; + int ret; + const struct gnutls_ia_server_credentials_st * cred = + _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL); + + if (cred == NULL) + return GNUTLS_E_INTERNAL_ERROR; + + do + { + char *avp; + size_t avplen; + + len = gnutls_ia_recv (session, buf, sizeof (buf)); + if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED || + len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) + { + ret = gnutls_ia_verify_endphase (session, buf); + if (ret < 0) + return ret; + } + + if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED) + continue; + else if (len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) + break; + + if (len < 0) + return len; + + avp = NULL; + avplen = 0; + + ret = cred->avp_func (session, cred->avp_ptr, buf, len, &avp, &avplen); + if (ret < 0) + { + int tmpret; + tmpret = gnutls_alert_send (session, GNUTLS_AL_FATAL, + GNUTLS_A_INNER_APPLICATION_FAILURE); + if (tmpret < 0) + gnutls_assert (); + return ret; + } + + msg_type = ret; + + if (msg_type != GNUTLS_IA_APPLICATION_PAYLOAD) + { + ret = gnutls_ia_endphase_send (session, msg_type == + GNUTLS_IA_FINAL_PHASE_FINISHED); + if (ret < 0) + return ret; + } + else + { + len = gnutls_ia_send (session, avp, avplen); + gnutls_free (avp); + if (len < 0) + return len; + } + } + while (1); + + return 0; +} + +/** + * gnutls_ia_handshake_p: + * @session: is a #gnutls_session_t structure. + * + * Predicate to be used after gnutls_handshake() to decide whether to + * invoke gnutls_ia_handshake(). Usable by both clients and servers. + * + * Return value: non-zero if TLS/IA handshake is expected, zero + * otherwise. + **/ +int +gnutls_ia_handshake_p (gnutls_session_t session) +{ + tls_ext_st *ext = &session->security_parameters.extensions; + + /* The other side didn't advertise TLS/IA. */ + if (ext->peer_mode == GNUTLS_IA_DISABLED) + return 0; + + /* We don't consider the inner phase optional. */ + if (!ext->inner_phase_optional) + return 1; + + /* This is not a resumed session. always require an inner + application. */ + if (!gnutls_session_is_resumed (session)) + return 1; + + /* Require an inner phase if the other end requested it. */ + return ext->peer_mode == GNUTLS_IA_APP_PHASE_ON_RESUMPTION_YES; +} + +/** + * gnutls_ia_handshake: + * @session: is a #gnutls_session_t structure. + * + * Perform a TLS/IA handshake. This should be called after + * gnutls_handshake() iff gnutls_ia_handshake_p(). + * + * Return 0 on success, or an error code. + **/ +int +gnutls_ia_handshake (gnutls_session_t session) +{ + int ret; + + if (session->security_parameters.entity == GNUTLS_CLIENT) + ret = _gnutls_ia_client_handshake (session); + else + ret = _gnutls_ia_server_handshake (session); + + return ret; +} + +/** + * gnutls_ia_allocate_client_credentials - Used to allocate an gnutls_ia_server_credentials_t structure + * @sc: is a pointer to an #gnutls_ia_server_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus this + * helper function is provided in order to allocate it. + * + * Adding this credential to a session will enable TLS/IA, and will + * require an Application Phase after the TLS handshake (if the server + * support TLS/IA). Use gnutls_ia_require_inner_phase() to toggle the + * TLS/IA mode. + * + * Returns 0 on success. + **/ +int +gnutls_ia_allocate_client_credentials (gnutls_ia_client_credentials_t * sc) +{ + *sc = gnutls_calloc (1, sizeof (**sc)); + + if (*sc == NULL) + return GNUTLS_E_MEMORY_ERROR; + + return 0; +} + +/** + * gnutls_ia_free_client_credentials - Used to free an allocated #gnutls_ia_client_credentials_t structure + * @sc: is an #gnutls_ia_client_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus this + * helper function is provided in order to free (deallocate) it. + * + **/ +void +gnutls_ia_free_client_credentials (gnutls_ia_client_credentials_t sc) +{ + gnutls_free (sc); +} + +/** + * gnutls_ia_set_client_avp_function - Used to set a AVP callback + * @cred: is a #gnutls_ia_client_credentials_t structure. + * @avp_func: is the callback function + * + * Set the TLS/IA AVP callback handler used for the session. + * + * The AVP callback is called to process AVPs received from the + * server, and to get a new AVP to send to the server. + * + * The callback's function form is: + * int (*avp_func) (gnutls_session_t session, void *ptr, + * const char *last, size_t lastlen, + * char **new, size_t *newlen); + * + * The @session parameter is the #gnutls_session_t structure + * corresponding to the current session. The @ptr parameter is the + * application hook pointer, set through + * gnutls_ia_set_client_avp_ptr(). The AVP received from the server + * is present in @last of @lastlen size, which will be %NULL on the + * first invocation. The newly allocated output AVP to send to the + * server should be placed in *@new of *@newlen size. + * + * The callback may invoke gnutls_ia_permute_inner_secret() to mix any + * generated session keys with the TLS/IA inner secret. + * + * Return 0 (%GNUTLS_IA_APPLICATION_PAYLOAD) on success, or a negative + * error code to abort the TLS/IA handshake. + * + * Note that the callback must use allocate the @new parameter using + * gnutls_malloc(), because it is released via gnutls_free() by the + * TLS/IA handshake function. + * + **/ +void +gnutls_ia_set_client_avp_function (gnutls_ia_client_credentials_t cred, + gnutls_ia_avp_func avp_func) +{ + cred->avp_func = avp_func; +} + +/** + * gnutls_ia_set_client_avp_ptr - Sets a pointer to be sent to TLS/IA callback + * @cred: is a #gnutls_ia_client_credentials_t structure. + * @ptr: is the pointer + * + * Sets the pointer that will be provided to the TLS/IA callback + * function as the first argument. + * + **/ +void +gnutls_ia_set_client_avp_ptr (gnutls_ia_client_credentials_t cred, void *ptr) +{ + cred->avp_ptr = ptr; +} + +/** + * gnutls_ia_get_client_avp_ptr - Returns the pointer which is sent to TLS/IA callback + * @cred: is a #gnutls_ia_client_credentials_t structure. + * + * Returns the pointer that will be provided to the TLS/IA callback + * function as the first argument. + * + **/ +void * +gnutls_ia_get_client_avp_ptr (gnutls_ia_client_credentials_t cred) +{ + return cred->avp_ptr; +} + +/** + * gnutls_ia_allocate_server_credentials - Used to allocate an gnutls_ia_server_credentials_t structure + * @sc: is a pointer to an #gnutls_ia_server_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus this + * helper function is provided in order to allocate it. + * + * Adding this credential to a session will enable TLS/IA, and will + * require an Application Phase after the TLS handshake (if the client + * support TLS/IA). Use gnutls_ia_require_inner_phase() to toggle the + * TLS/IA mode. + * + * Returns 0 on success. + **/ +int +gnutls_ia_allocate_server_credentials (gnutls_ia_server_credentials_t * sc) +{ + *sc = gnutls_calloc (1, sizeof (**sc)); + + if (*sc == NULL) + return GNUTLS_E_MEMORY_ERROR; + + return 0; +} + +/** + * gnutls_ia_free_server_credentials - Used to free an allocated #gnutls_ia_server_credentials_t structure + * @sc: is an #gnutls_ia_server_credentials_t structure. + * + * This structure is complex enough to manipulate directly thus this + * helper function is provided in order to free (deallocate) it. + * + **/ +void +gnutls_ia_free_server_credentials (gnutls_ia_server_credentials_t sc) +{ + gnutls_free (sc); +} + +/** + * gnutls_ia_set_server_credentials_function - Used to set a AVP callback + * @cred: is a #gnutls_ia_server_credentials_t structure. + * @func: is the callback function + * + * Set the TLS/IA AVP callback handler used for the session. + * + * The callback's function form is: + * int (*avp_func) (gnutls_session_t session, void *ptr, + * const char *last, size_t lastlen, + * char **new, size_t *newlen); + * + * The @session parameter is the #gnutls_session_t structure + * corresponding to the current session. The @ptr parameter is the + * application hook pointer, set through + * gnutls_ia_set_server_avp_ptr(). The AVP received from the client + * is present in @last of @lastlen size. The newly allocated output + * AVP to send to the client should be placed in *@new of *@newlen + * size. + * + * The AVP callback is called to process incoming AVPs from the + * client, and to get a new AVP to send to the client. It can also be + * used to instruct the TLS/IA handshake to do go into the + * Intermediate or Final phases. It return a negative error code, or + * an #gnutls_ia_apptype_t message type. + * + * The callback may invoke gnutls_ia_permute_inner_secret() to mix any + * generated session keys with the TLS/IA inner secret. + * + * Specifically, return %GNUTLS_IA_APPLICATION_PAYLOAD (0) to send + * another AVP to the client, return + * %GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED (1) to indicate that an + * IntermediatePhaseFinished message should be sent, and return + * %GNUTLS_IA_FINAL_PHASE_FINISHED (2) to indicate that an + * FinalPhaseFinished message should be sent. In the last two cases, + * the contents of the @new and @newlen parameter is not used. + * + * Note that the callback must use allocate the @new parameter using + * gnutls_malloc(), because it is released via gnutls_free() by the + * TLS/IA handshake function. + **/ +void +gnutls_ia_set_server_avp_function (gnutls_ia_server_credentials_t cred, + gnutls_ia_avp_func avp_func) +{ + cred->avp_func = avp_func; +} + +/** + * gnutls_ia_set_server_avp_ptr - Sets a pointer to be sent to TLS/IA callback + * @cred: is a #gnutls_ia_client_credentials_t structure. + * @ptr: is the pointer + * + * Sets the pointer that will be provided to the TLS/IA callback + * function as the first argument. + * + **/ +void +gnutls_ia_set_server_avp_ptr (gnutls_ia_server_credentials_t cred, void *ptr) +{ + cred->avp_ptr = ptr; +} + +/** + * gnutls_ia_get_server_avp_ptr - Returns the pointer which is sent to TLS/IA callback + * @cred: is a #gnutls_ia_client_credentials_t structure. + * + * Returns the pointer that will be provided to the TLS/IA callback + * function as the first argument. + * + **/ +void * +gnutls_ia_get_server_avp_ptr (gnutls_ia_server_credentials_t cred) +{ + return cred->avp_ptr; +} + +/** + * gnutls_ia_require_inner_phase - Set if a TLS/IA inner phase is required + * @session: is a #gnutls_session_t structure. + * @require: non-zero if an inner application phase should be required. + * + * Specify whether a TLS/IA Inner Application Phase will be required + * or not. If the TLS session is resumed, it is possibly to optimize + * away the inner application phase by calling this function and + * specify a zero value for @require. + * + * For this function to have any effect, it must be called before + * gnutls_handshake(). + * + * Whether to invoke the TLS/IA handshake will also depend on whether + * the client supports or requested TLS/IA. A server should thus use + * gnutls_ia_handshake_p() to decide whether to call + * gnutls_ia_handshake() or not. + **/ +void +gnutls_ia_require_inner_phase (gnutls_session_t session, int require) +{ + /* XXX. This function should ideally take a TLS/IA credential, but + we can't do that, since we don't want the LGPL'd + lib/ext_inner_applicaiton.c to call/inspect GPL'd + functions/structures. */ + session->security_parameters.extensions.inner_phase_optional = !require; +} |