/* * Copyright (C) 2005, 2006, 2008, 2010 Free Software Foundation, Inc. * * 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 3 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 this program. If not, see * . * */ #include "gnutls_int.h" #include "gnutls_auth.h" #include "gnutls_errors.h" #include "gnutls_num.h" #include "ext_inner_application.h" #include #define NO 0 #define YES 1 static int _gnutls_inner_application_recv_params (gnutls_session_t session, const opaque * data, size_t data_size); static int _gnutls_inner_application_send_params (gnutls_session_t session, opaque * data, size_t); static int ia_unpack (gnutls_buffer_st * ps, extension_priv_data_t * _priv); static int ia_pack (extension_priv_data_t _priv, gnutls_buffer_st * ps); static void ia_deinit_data (extension_priv_data_t priv); extension_entry_st ext_mod_ia = { .name = "INNER APPLICATION", .type = GNUTLS_EXTENSION_INNER_APPLICATION, .parse_type = GNUTLS_EXT_TLS, .recv_func = _gnutls_inner_application_recv_params, .send_func = _gnutls_inner_application_send_params, .pack_func = ia_pack, .unpack_func = ia_unpack, .deinit_func = ia_deinit_data, }; static int _gnutls_inner_application_recv_params (gnutls_session_t session, const opaque * data, size_t data_size) { extension_priv_data_t epriv; ia_ext_st *priv; int ret; if (data_size != 1) { gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } ret = _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_INNER_APPLICATION, &epriv); if (ret < 0) { priv = gnutls_calloc (1, sizeof (*priv)); if (priv == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } epriv.ptr = priv; _gnutls_ext_set_session_data (session, GNUTLS_EXTENSION_INNER_APPLICATION, epriv); } else priv = epriv.ptr; priv->flags |= IA_PEER_ENABLE; priv->flags &= ~IA_PEER_ALLOW_SKIP; switch ((unsigned char) *data) { case NO: /* Peer's ia_on_resume == no */ priv->flags |= IA_PEER_ALLOW_SKIP; break; case YES: break; default: gnutls_assert (); } return 0; } /* returns data_size or a negative number on failure */ static int _gnutls_inner_application_send_params (gnutls_session_t session, opaque * data, size_t data_size) { extension_priv_data_t epriv; ia_ext_st *priv = NULL; int ret; ret = _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_INNER_APPLICATION, &epriv); if (ret < 0) { priv = gnutls_calloc (1, sizeof (*priv)); if (priv == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } epriv.ptr = priv; _gnutls_ext_set_session_data (session, GNUTLS_EXTENSION_INNER_APPLICATION, epriv); } else priv = epriv.ptr; /* Set ext->gnutls_ia_enable depending on whether we have a TLS/IA credential in the session. */ if (session->security_parameters.entity == GNUTLS_CLIENT) { gnutls_ia_client_credentials_t cred = (gnutls_ia_client_credentials_t) _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL); if (cred) priv->flags |= IA_ENABLE; } else /* SERVER */ { gnutls_ia_server_credentials_t cred; cred = (gnutls_ia_server_credentials_t) _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL); if (cred) priv->flags |= IA_PEER_ENABLE; } /* If we don't want gnutls_ia locally, or we are a server and the * client doesn't want it, don't advertise TLS/IA support at all, as * required. */ if (!(priv->flags & IA_ENABLE)) return 0; if (session->security_parameters.entity == GNUTLS_SERVER && !(priv->flags & IA_PEER_ENABLE)) return 0; /* We'll advertise. Check if there's room in the hello buffer. */ if (data_size < 1) { gnutls_assert (); return GNUTLS_E_SHORT_MEMORY_BUFFER; } /* default: require new application phase */ *data = YES; if (session->security_parameters.entity == GNUTLS_CLIENT) { /* Client: value follows local setting */ if (priv->flags & IA_ALLOW_SKIP) *data = NO; } else { /* Server: value follows local setting and client's setting, but only * if we are resuming. * * XXX Can server test for resumption at this stage? * * Ai! It seems that read_client_hello only calls parse_extensions if * we're NOT resuming! That would make us automatically violate the IA * draft; if we're resuming, we must first learn what the client wants * -- IA or no IA -- and then prepare our response. Right now we'll * always skip IA on resumption, because recv_ext isn't even called * to record the peer's support for IA at all. Simon? */ if ((priv->flags & IA_ALLOW_SKIP) && (priv->flags & IA_PEER_ALLOW_SKIP) && session->internals.resumed == RESUME_TRUE) *data = NO; } return 1; } static void ia_deinit_data (extension_priv_data_t priv) { gnutls_free (priv.ptr); } static int ia_pack (extension_priv_data_t epriv, gnutls_buffer_st * ps) { ia_ext_st *priv = epriv.ptr; int ret; BUFFER_APPEND_NUM (ps, priv->flags); BUFFER_APPEND_PFX (ps, priv->inner_secret, GNUTLS_MASTER_SIZE); return 0; } static int ia_unpack (gnutls_buffer_st * ps, extension_priv_data_t * _priv) { ia_ext_st *priv; int size, ret; extension_priv_data_t epriv; priv = gnutls_calloc (1, sizeof (*priv)); if (priv == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } BUFFER_POP_NUM (ps, priv->flags); BUFFER_POP_NUM (ps, size); if (size != GNUTLS_MASTER_SIZE) { gnutls_assert (); return GNUTLS_E_PARSING_ERROR; } BUFFER_POP (ps, priv->inner_secret, GNUTLS_MASTER_SIZE); epriv.ptr = priv; *_priv = epriv; return 0; error: gnutls_free (priv); return ret; }