Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2017-2018 Red Hat, Inc. |
3 | | * |
4 | | * Author: Nikos Mavrogiannopoulos |
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 License |
19 | | * along with this program. If not, see <https://www.gnu.org/licenses/> |
20 | | * |
21 | | */ |
22 | | |
23 | | #include "gnutls_int.h" |
24 | | #include "hello_ext.h" |
25 | | #include "errors.h" |
26 | | #include "extv.h" |
27 | | |
28 | | /* Iterates through all extensions found, and calls the cb() |
29 | | * function with their data */ |
30 | | int _gnutls_extv_parse(void *ctx, |
31 | | gnutls_ext_raw_process_func cb, |
32 | | const uint8_t * data, int data_size) |
33 | 0 | { |
34 | 0 | int next, ret; |
35 | 0 | int pos = 0; |
36 | 0 | uint16_t tls_id; |
37 | 0 | const uint8_t *sdata; |
38 | 0 | uint16_t size; |
39 | |
|
40 | 0 | if (data_size == 0) |
41 | 0 | return 0; |
42 | | |
43 | 0 | DECR_LENGTH_RET(data_size, 2, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); |
44 | 0 | next = _gnutls_read_uint16(data); |
45 | 0 | pos += 2; |
46 | |
|
47 | 0 | DECR_LENGTH_RET(data_size, next, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); |
48 | | |
49 | 0 | if (next == 0 && data_size == 0) /* field is present, but has zero length? Ignore it. */ |
50 | 0 | return 0; |
51 | 0 | else if (data_size > 0) /* forbid unaccounted data */ |
52 | 0 | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); |
53 | | |
54 | 0 | do { |
55 | 0 | DECR_LENGTH_RET(next, 2, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); |
56 | 0 | tls_id = _gnutls_read_uint16(&data[pos]); |
57 | 0 | pos += 2; |
58 | |
|
59 | 0 | DECR_LENGTH_RET(next, 2, GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); |
60 | 0 | size = _gnutls_read_uint16(&data[pos]); |
61 | 0 | pos += 2; |
62 | |
|
63 | 0 | DECR_LENGTH_RET(next, size, |
64 | 0 | GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); |
65 | 0 | sdata = &data[pos]; |
66 | 0 | pos += size; |
67 | |
|
68 | 0 | ret = cb(ctx, tls_id, sdata, size); |
69 | 0 | if (ret < 0) |
70 | 0 | return gnutls_assert_val(ret); |
71 | 0 | } |
72 | 0 | while (next > 2); |
73 | | |
74 | | /* forbid leftovers */ |
75 | 0 | if (next > 0) |
76 | 0 | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH); |
77 | | |
78 | 0 | return 0; |
79 | 0 | } |
80 | | |
81 | 0 | #define HANDSHAKE_SESSION_ID_POS (34) |
82 | | /** |
83 | | * gnutls_ext_raw_parse: |
84 | | * @ctx: a pointer to pass to callback function |
85 | | * @cb: callback function to process each extension found |
86 | | * @data: TLS extension data |
87 | | * @flags: should be zero or %GNUTLS_EXT_RAW_FLAG_TLS_CLIENT_HELLO or %GNUTLS_EXT_RAW_FLAG_DTLS_CLIENT_HELLO |
88 | | * |
89 | | * This function iterates through the TLS extensions as passed in |
90 | | * @data, passing the individual extension data to callback. The |
91 | | * @data must conform to Extension extensions<0..2^16-1> format. |
92 | | * |
93 | | * If flags is %GNUTLS_EXT_RAW_TLS_FLAG_CLIENT_HELLO then this function |
94 | | * will parse the extension data from the position, as if the packet in |
95 | | * @data is a client hello (without record or handshake headers) - |
96 | | * as provided by gnutls_handshake_set_hook_function(). |
97 | | * |
98 | | * The return value of the callback will be propagated. |
99 | | * |
100 | | * Returns: %GNUTLS_E_SUCCESS on success, or an error code. On unknown |
101 | | * flags it returns %GNUTLS_E_INVALID_REQUEST. |
102 | | * |
103 | | * Since: 3.6.3 |
104 | | **/ |
105 | | int gnutls_ext_raw_parse(void *ctx, gnutls_ext_raw_process_func cb, |
106 | | const gnutls_datum_t * data, unsigned int flags) |
107 | 0 | { |
108 | 0 | if (flags & GNUTLS_EXT_RAW_FLAG_TLS_CLIENT_HELLO) { |
109 | 0 | size_t size = data->size; |
110 | 0 | size_t len; |
111 | 0 | uint8_t *p = data->data; |
112 | |
|
113 | 0 | DECR_LEN(size, HANDSHAKE_SESSION_ID_POS); |
114 | | |
115 | 0 | if (p[0] != 0x03) |
116 | 0 | return |
117 | 0 | gnutls_assert_val |
118 | 0 | (GNUTLS_E_UNSUPPORTED_VERSION_PACKET); |
119 | | |
120 | 0 | p += HANDSHAKE_SESSION_ID_POS; |
121 | | |
122 | | /* skip session id */ |
123 | 0 | DECR_LEN(size, 1); |
124 | 0 | len = p[0]; |
125 | 0 | p++; |
126 | 0 | DECR_LEN(size, len); |
127 | 0 | p += len; |
128 | | |
129 | | /* CipherSuites */ |
130 | 0 | DECR_LEN(size, 2); |
131 | 0 | len = _gnutls_read_uint16(p); |
132 | 0 | p += 2; |
133 | 0 | DECR_LEN(size, len); |
134 | 0 | p += len; |
135 | | |
136 | | /* legacy_compression_methods */ |
137 | 0 | DECR_LEN(size, 1); |
138 | 0 | len = p[0]; |
139 | 0 | p++; |
140 | 0 | DECR_LEN(size, len); |
141 | 0 | p += len; |
142 | |
|
143 | 0 | if (size == 0) |
144 | 0 | return |
145 | 0 | gnutls_assert_val |
146 | 0 | (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); |
147 | | |
148 | 0 | return _gnutls_extv_parse(ctx, cb, p, size); |
149 | 0 | } else if (flags & GNUTLS_EXT_RAW_FLAG_DTLS_CLIENT_HELLO) { |
150 | 0 | size_t size = data->size; |
151 | 0 | size_t len; |
152 | 0 | uint8_t *p = data->data; |
153 | |
|
154 | 0 | DECR_LEN(size, HANDSHAKE_SESSION_ID_POS); |
155 | | |
156 | 0 | if (p[0] != 254) |
157 | 0 | return |
158 | 0 | gnutls_assert_val |
159 | 0 | (GNUTLS_E_UNSUPPORTED_VERSION_PACKET); |
160 | | |
161 | 0 | p += HANDSHAKE_SESSION_ID_POS; |
162 | | |
163 | | /* skip session id */ |
164 | 0 | DECR_LEN(size, 1); |
165 | 0 | len = p[0]; |
166 | 0 | p++; |
167 | 0 | DECR_LEN(size, len); |
168 | 0 | p += len; |
169 | | |
170 | | /* skip cookie */ |
171 | 0 | DECR_LEN(size, 1); |
172 | 0 | len = p[0]; |
173 | 0 | p++; |
174 | 0 | DECR_LEN(size, len); |
175 | 0 | p += len; |
176 | | |
177 | | /* CipherSuites */ |
178 | 0 | DECR_LEN(size, 2); |
179 | 0 | len = _gnutls_read_uint16(p); |
180 | 0 | p += 2; |
181 | 0 | DECR_LEN(size, len); |
182 | 0 | p += len; |
183 | | |
184 | | /* legacy_compression_methods */ |
185 | 0 | DECR_LEN(size, 1); |
186 | 0 | len = p[0]; |
187 | 0 | p++; |
188 | 0 | DECR_LEN(size, len); |
189 | 0 | p += len; |
190 | |
|
191 | 0 | if (size == 0) |
192 | 0 | return |
193 | 0 | gnutls_assert_val |
194 | 0 | (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); |
195 | | |
196 | 0 | return _gnutls_extv_parse(ctx, cb, p, size); |
197 | 0 | } |
198 | | |
199 | 0 | if (flags != 0) |
200 | 0 | return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); |
201 | | |
202 | 0 | return _gnutls_extv_parse(ctx, cb, data->data, data->size); |
203 | 0 | } |
204 | | |
205 | | /* Returns: |
206 | | * * On success the number of bytes appended (always positive), or zero if not sent |
207 | | * * On failure, a negative error code. |
208 | | */ |
209 | | int _gnutls_extv_append(gnutls_buffer_st * buf, |
210 | | uint16_t tls_id, |
211 | | void *ctx, int (*cb)(void *ctx, gnutls_buffer_st * buf)) |
212 | 0 | { |
213 | 0 | int size_pos, appended, ret; |
214 | 0 | size_t size_prev; |
215 | |
|
216 | 0 | ret = _gnutls_buffer_append_prefix(buf, 16, tls_id); |
217 | 0 | if (ret < 0) |
218 | 0 | return gnutls_assert_val(ret); |
219 | | |
220 | 0 | size_pos = buf->length; |
221 | 0 | ret = _gnutls_buffer_append_prefix(buf, 16, 0); |
222 | 0 | if (ret < 0) |
223 | 0 | return gnutls_assert_val(ret); |
224 | | |
225 | 0 | size_prev = buf->length; |
226 | 0 | ret = cb(ctx, buf); |
227 | 0 | if (ret < 0 && ret != GNUTLS_E_INT_RET_0) { |
228 | 0 | return gnutls_assert_val(ret); |
229 | 0 | } |
230 | | |
231 | | /* returning GNUTLS_E_INT_RET_0 means to send an empty |
232 | | * extension of this type. |
233 | | */ |
234 | 0 | appended = buf->length - size_prev; |
235 | |
|
236 | 0 | if (appended > 0 || ret == GNUTLS_E_INT_RET_0) { |
237 | 0 | if (ret == GNUTLS_E_INT_RET_0) |
238 | 0 | appended = 0; |
239 | | |
240 | | /* write the real size */ |
241 | 0 | _gnutls_write_uint16(appended, &buf->data[size_pos]); |
242 | 0 | } else if (appended == 0) { |
243 | 0 | buf->length -= 4; /* reset type and size */ |
244 | 0 | return 0; |
245 | 0 | } |
246 | | |
247 | 0 | return appended + 4; |
248 | 0 | } |