Tizen 2.0 Release
[external/libgnutls26.git] / lib / gnutls_supplemental.c
1 /*
2  * Copyright (C) 2007, 2008, 2010 Free Software Foundation, Inc.
3  *
4  * Author: Simon Josefsson
5  *
6  * This file is part of GnuTLS.
7  *
8  * The GnuTLS is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library 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  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
21  * USA
22  *
23  */
24
25 /* This file contains support functions for 'TLS Handshake Message for
26  * Supplemental Data' (RFC 4680).
27  *
28  * The idea here is simple.  gnutls_handshake() in gnuts_handshake.c
29  * will call _gnutls_gen_supplemental and _gnutls_parse_supplemental
30  * when some extension requested that supplemental data be sent or
31  * received.  Extension request this by setting the flags
32  * do_recv_supplemental or do_send_supplemental in the session.
33  *
34  * The functions in this file iterate through the _gnutls_supplemental
35  * array, and calls the send/recv functions for each respective data
36  * type.
37  *
38  * The receive function of each data type is responsible for decoding
39  * its own data.  If the extension did not expect to receive
40  * supplemental data, it should return GNUTLS_E_UNEXPECTED_PACKET.
41  * Otherwise, it just parse the data as normal.
42  *
43  * The send function needs to append the 2-byte data format type, and
44  * append the 2-byte length of its data, and the data.  If it doesn't
45  * want to send any data, it is fine to return without doing anything.
46  */
47
48 #include "gnutls_int.h"
49 #include "gnutls_supplemental.h"
50 #include "gnutls_errors.h"
51 #include "gnutls_num.h"
52
53 typedef int (*supp_recv_func) (gnutls_session_t session,
54                                const opaque * data, size_t data_size);
55 typedef int (*supp_send_func) (gnutls_session_t session,
56                                gnutls_buffer_st * buf);
57
58 typedef struct
59 {
60   const char *name;
61   gnutls_supplemental_data_format_type_t type;
62   supp_recv_func supp_recv_func;
63   supp_send_func supp_send_func;
64 } gnutls_supplemental_entry;
65
66 gnutls_supplemental_entry _gnutls_supplemental[] = {
67   {0, 0, 0, 0}
68 };
69
70 /**
71  * gnutls_supplemental_get_name:
72  * @type: is a supplemental data format type
73  *
74  * Convert a #gnutls_supplemental_data_format_type_t value to a
75  * string.
76  *
77  * Returns: a string that contains the name of the specified
78  *   supplemental data format type, or %NULL for unknown types.
79  **/
80 const char *
81 gnutls_supplemental_get_name (gnutls_supplemental_data_format_type_t type)
82 {
83   gnutls_supplemental_entry *p;
84
85   for (p = _gnutls_supplemental; p->name != NULL; p++)
86     if (p->type == type)
87       return p->name;
88
89   return NULL;
90 }
91
92 static supp_recv_func
93 get_supp_func_recv (gnutls_supplemental_data_format_type_t type)
94 {
95   gnutls_supplemental_entry *p;
96
97   for (p = _gnutls_supplemental; p->name != NULL; p++)
98     if (p->type == type)
99       return p->supp_recv_func;
100
101   return NULL;
102 }
103
104 int
105 _gnutls_gen_supplemental (gnutls_session_t session, gnutls_buffer_st * buf)
106 {
107   gnutls_supplemental_entry *p;
108   int ret;
109
110   /* Make room for 3 byte length field. */
111   ret = _gnutls_buffer_append_data (buf, "\0\0\0", 3);
112   if (ret < 0)
113     {
114       gnutls_assert ();
115       return ret;
116     }
117
118   for (p = _gnutls_supplemental; p->name; p++)
119     {
120       supp_send_func supp_send = p->supp_send_func;
121       size_t sizepos = buf->length;
122
123       /* Make room for supplement type and length byte length field. */
124       ret = _gnutls_buffer_append_data (buf, "\0\0\0\0", 4);
125       if (ret < 0)
126         {
127           gnutls_assert ();
128           return ret;
129         }
130
131       ret = supp_send (session, buf);
132       if (ret < 0)
133         {
134           gnutls_assert ();
135           return ret;
136         }
137
138       /* If data were added, store type+length, otherwise reset. */
139       if (buf->length > sizepos + 4)
140         {
141           buf->data[sizepos] = 0;
142           buf->data[sizepos + 1] = p->type;
143           buf->data[sizepos + 2] = ((buf->length - sizepos - 4) >> 8) & 0xFF;
144           buf->data[sizepos + 3] = (buf->length - sizepos - 4) & 0xFF;
145         }
146       else
147         buf->length -= 4;
148     }
149
150   buf->data[0] = ((buf->length - 3) >> 16) & 0xFF;
151   buf->data[1] = ((buf->length - 3) >> 8) & 0xFF;
152   buf->data[2] = (buf->length - 3) & 0xFF;
153
154   _gnutls_debug_log ("EXT[%p]: Sending %d bytes of supplemental data\n",
155                      session, (int) buf->length);
156
157   return buf->length;
158 }
159
160 int
161 _gnutls_parse_supplemental (gnutls_session_t session,
162                             const uint8_t * data, int datalen)
163 {
164   const opaque *p = data;
165   ssize_t dsize = datalen;
166   size_t total_size;
167
168   DECR_LEN (dsize, 3);
169   total_size = _gnutls_read_uint24 (p);
170   p += 3;
171
172   if (dsize != (ssize_t) total_size)
173     {
174       gnutls_assert ();
175       return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
176     }
177
178   do
179     {
180       uint16_t supp_data_type;
181       uint16_t supp_data_length;
182       supp_recv_func recv_func;
183
184       DECR_LEN (dsize, 2);
185       supp_data_type = _gnutls_read_uint16 (p);
186       p += 2;
187
188       DECR_LEN (dsize, 2);
189       supp_data_length = _gnutls_read_uint16 (p);
190       p += 2;
191
192       _gnutls_debug_log ("EXT[%p]: Got supplemental type=%02x length=%d\n",
193                          session, supp_data_type, supp_data_length);
194
195       recv_func = get_supp_func_recv (supp_data_type);
196       if (recv_func)
197         {
198           int ret = recv_func (session, p, supp_data_length);
199           if (ret < 0)
200             {
201               gnutls_assert ();
202               return ret;
203             }
204         }
205       else
206         {
207           gnutls_assert ();
208           return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
209         }
210
211       DECR_LEN (dsize, supp_data_length);
212       p += supp_data_length;
213     }
214   while (dsize > 0);
215
216   return 0;
217 }