tizen 2.3.1 release
[external/libgnutls26.git] / libextra / ext_inner_application.c
1 /*
2  * Copyright (C) 2005, 2006, 2008, 2010 Free Software Foundation, Inc.
3  *
4  * Author: Simon Josefsson
5  *
6  * This file is part of GnuTLS-EXTRA.
7  *
8  * GnuTLS-extra is free software: you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation, either version 3 of the
11  * License, or (at your option) any later version.
12  *
13  * GnuTLS-extra is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see
20  * <http://www.gnu.org/licenses/>.
21  *
22  */
23
24 #include "gnutls_int.h"
25 #include "gnutls_auth.h"
26 #include "gnutls_errors.h"
27 #include "gnutls_num.h"
28 #include "ext_inner_application.h"
29 #include <gnutls/extra.h>
30
31 #define NO 0
32 #define YES 1
33
34 static int _gnutls_inner_application_recv_params (gnutls_session_t session,
35                                                   const opaque * data,
36                                                   size_t data_size);
37 static int _gnutls_inner_application_send_params (gnutls_session_t session,
38                                                   opaque * data, size_t);
39 static int ia_unpack (gnutls_buffer_st * ps, extension_priv_data_t * _priv);
40 static int ia_pack (extension_priv_data_t _priv, gnutls_buffer_st * ps);
41 static void ia_deinit_data (extension_priv_data_t priv);
42
43 extension_entry_st ext_mod_ia = {
44   .name = "INNER APPLICATION",
45   .type = GNUTLS_EXTENSION_INNER_APPLICATION,
46   .parse_type = GNUTLS_EXT_TLS,
47
48   .recv_func = _gnutls_inner_application_recv_params,
49   .send_func = _gnutls_inner_application_send_params,
50   .pack_func = ia_pack,
51   .unpack_func = ia_unpack,
52   .deinit_func = ia_deinit_data,
53 };
54
55 static int
56 _gnutls_inner_application_recv_params (gnutls_session_t session,
57                                        const opaque * data, size_t data_size)
58 {
59   extension_priv_data_t epriv;
60   ia_ext_st *priv;
61   int ret;
62
63   if (data_size != 1)
64     {
65       gnutls_assert ();
66       return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
67     }
68
69   ret =
70     _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_INNER_APPLICATION,
71                                   &epriv);
72   if (ret < 0)
73     {
74       priv = gnutls_calloc (1, sizeof (*priv));
75       if (priv == NULL)
76         {
77           gnutls_assert ();
78           return GNUTLS_E_MEMORY_ERROR;
79         }
80
81       epriv.ptr = priv;
82       _gnutls_ext_set_session_data (session,
83                                     GNUTLS_EXTENSION_INNER_APPLICATION,
84                                     epriv);
85     }
86   else
87     priv = epriv.ptr;
88
89   priv->flags |= IA_PEER_ENABLE;
90   priv->flags &= ~IA_PEER_ALLOW_SKIP;
91
92   switch ((unsigned char) *data)
93     {
94     case NO:                   /* Peer's ia_on_resume == no */
95       priv->flags |= IA_PEER_ALLOW_SKIP;
96       break;
97
98     case YES:
99       break;
100
101     default:
102       gnutls_assert ();
103     }
104
105
106   return 0;
107 }
108
109
110 /* returns data_size or a negative number on failure
111  */
112 static int
113 _gnutls_inner_application_send_params (gnutls_session_t session,
114                                        opaque * data, size_t data_size)
115 {
116   extension_priv_data_t epriv;
117   ia_ext_st *priv = NULL;
118   int ret;
119
120   ret =
121     _gnutls_ext_get_session_data (session, GNUTLS_EXTENSION_INNER_APPLICATION,
122                                   &epriv);
123   if (ret < 0)
124     {
125       priv = gnutls_calloc (1, sizeof (*priv));
126       if (priv == NULL)
127         {
128           gnutls_assert ();
129           return GNUTLS_E_MEMORY_ERROR;
130         }
131
132       epriv.ptr = priv;
133       _gnutls_ext_set_session_data (session,
134                                     GNUTLS_EXTENSION_INNER_APPLICATION,
135                                     epriv);
136     }
137   else
138     priv = epriv.ptr;
139
140   /* Set ext->gnutls_ia_enable depending on whether we have a TLS/IA
141      credential in the session. */
142
143   if (session->security_parameters.entity == GNUTLS_CLIENT)
144     {
145       gnutls_ia_client_credentials_t cred = (gnutls_ia_client_credentials_t)
146         _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL);
147
148       if (cred)
149         priv->flags |= IA_ENABLE;
150     }
151   else                          /* SERVER */
152     {
153       gnutls_ia_server_credentials_t cred;
154
155       cred = (gnutls_ia_server_credentials_t)
156         _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL);
157
158       if (cred)
159         priv->flags |= IA_PEER_ENABLE;
160     }
161
162   /* If we don't want gnutls_ia locally, or we are a server and the
163    * client doesn't want it, don't advertise TLS/IA support at all, as
164    * required. */
165
166   if (!(priv->flags & IA_ENABLE))
167     return 0;
168
169   if (session->security_parameters.entity == GNUTLS_SERVER &&
170       !(priv->flags & IA_PEER_ENABLE))
171     return 0;
172
173   /* We'll advertise. Check if there's room in the hello buffer. */
174
175   if (data_size < 1)
176     {
177       gnutls_assert ();
178       return GNUTLS_E_SHORT_MEMORY_BUFFER;
179     }
180
181   /* default: require new application phase */
182
183   *data = YES;
184
185   if (session->security_parameters.entity == GNUTLS_CLIENT)
186     {
187
188       /* Client: value follows local setting */
189
190       if (priv->flags & IA_ALLOW_SKIP)
191         *data = NO;
192     }
193   else
194     {
195
196       /* Server: value follows local setting and client's setting, but only
197        * if we are resuming.
198        *
199        * XXX Can server test for resumption at this stage?
200        *
201        * Ai! It seems that read_client_hello only calls parse_extensions if
202        * we're NOT resuming! That would make us automatically violate the IA
203        * draft; if we're resuming, we must first learn what the client wants
204        * -- IA or no IA -- and then prepare our response. Right now we'll
205        * always skip IA on resumption, because recv_ext isn't even called
206        * to record the peer's support for IA at all. Simon? */
207
208       if ((priv->flags & IA_ALLOW_SKIP) &&
209           (priv->flags & IA_PEER_ALLOW_SKIP) &&
210           session->internals.resumed == RESUME_TRUE)
211         *data = NO;
212     }
213
214   return 1;
215 }
216
217 static void
218 ia_deinit_data (extension_priv_data_t priv)
219 {
220   gnutls_free (priv.ptr);
221 }
222
223 static int
224 ia_pack (extension_priv_data_t epriv, gnutls_buffer_st * ps)
225 {
226   ia_ext_st *priv = epriv.ptr;
227   int ret;
228
229   BUFFER_APPEND_NUM (ps, priv->flags);
230   BUFFER_APPEND_PFX (ps, priv->inner_secret, GNUTLS_MASTER_SIZE);
231
232   return 0;
233 }
234
235 static int
236 ia_unpack (gnutls_buffer_st * ps, extension_priv_data_t * _priv)
237 {
238   ia_ext_st *priv;
239   int size, ret;
240   extension_priv_data_t epriv;
241
242   priv = gnutls_calloc (1, sizeof (*priv));
243   if (priv == NULL)
244     {
245       gnutls_assert ();
246       return GNUTLS_E_MEMORY_ERROR;
247     }
248
249   BUFFER_POP_NUM (ps, priv->flags);
250   BUFFER_POP_NUM (ps, size);
251   if (size != GNUTLS_MASTER_SIZE)
252     {
253       gnutls_assert ();
254       return GNUTLS_E_PARSING_ERROR;
255     }
256   BUFFER_POP (ps, priv->inner_secret, GNUTLS_MASTER_SIZE);
257
258   epriv.ptr = priv;
259   *_priv = epriv;
260
261   return 0;
262
263 error:
264   gnutls_free (priv);
265   return ret;
266 }