/src/gnutls/lib/tls13/certificate.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2017 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 "compress.h" |
25 | | #include "errors.h" |
26 | | #include "extv.h" |
27 | | #include "handshake.h" |
28 | | #include "tls13/certificate.h" |
29 | | #include "auth/cert.h" |
30 | | #include "mbuffers.h" |
31 | | #include "ext/compress_certificate.h" |
32 | | #include "ext/status_request.h" |
33 | | |
34 | | static int parse_cert_extension(void *ctx, unsigned tls_id, const uint8_t *data, |
35 | | unsigned data_size); |
36 | | static int parse_cert_list(gnutls_session_t session, uint8_t *data, |
37 | | size_t data_size); |
38 | | static int compress_certificate(gnutls_buffer_st *buf, unsigned cert_pos_mark, |
39 | | gnutls_compression_method_t comp_method); |
40 | | static int decompress_certificate(gnutls_session_t session, |
41 | | gnutls_buffer_st *buf); |
42 | | |
43 | | int _gnutls13_recv_certificate(gnutls_session_t session) |
44 | 4.79k | { |
45 | 4.79k | int ret, err, decompress_cert = 0; |
46 | 4.79k | gnutls_buffer_st buf; |
47 | 4.79k | unsigned optional = 0; |
48 | | |
49 | 4.79k | if (!session->internals.initial_negotiation_completed && |
50 | 4.79k | session->internals.hsk_flags & HSK_PSK_SELECTED) |
51 | 19 | return 0; |
52 | | |
53 | 4.77k | if (session->security_parameters.entity == GNUTLS_SERVER) { |
54 | | /* if we didn't request a certificate, there will not be any */ |
55 | 390 | if (session->internals.send_cert_req == 0) |
56 | 390 | return 0; |
57 | | |
58 | 0 | if (session->internals.send_cert_req != GNUTLS_CERT_REQUIRE) |
59 | 0 | optional = 1; |
60 | 0 | } |
61 | | |
62 | 4.38k | ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_PKT, |
63 | 4.38k | 0, &buf); |
64 | 4.38k | if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET) { |
65 | | /* check if we received compressed certificate */ |
66 | 35 | err = _gnutls_recv_handshake( |
67 | 35 | session, GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT, 0, |
68 | 35 | &buf); |
69 | 35 | if (err >= 0) { |
70 | | /* fail if we receive unsolicited compressed certificate */ |
71 | 15 | if (!(session->internals.hsk_flags & |
72 | 15 | HSK_COMP_CRT_REQ_SENT)) |
73 | 15 | return gnutls_assert_val( |
74 | 15 | GNUTLS_E_UNEXPECTED_PACKET); |
75 | | |
76 | 0 | decompress_cert = 1; |
77 | 0 | ret = err; |
78 | 0 | } |
79 | 35 | } |
80 | 4.36k | if (ret < 0) { |
81 | 187 | if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET && |
82 | 20 | session->internals.send_cert_req) |
83 | 0 | return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND); |
84 | | |
85 | 187 | return gnutls_assert_val(ret); |
86 | 187 | } |
87 | | |
88 | 4.18k | if (buf.length == 0) { |
89 | 6 | gnutls_assert(); |
90 | 6 | ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; |
91 | 6 | goto cleanup; |
92 | 6 | } |
93 | | |
94 | 4.17k | if (decompress_cert) { |
95 | 0 | ret = decompress_certificate(session, &buf); |
96 | 0 | if (ret < 0) { |
97 | 0 | gnutls_assert(); |
98 | 0 | goto cleanup; |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | 4.17k | if (session->internals.initial_negotiation_completed && |
103 | 0 | session->internals.post_handshake_cr_context.size > 0) { |
104 | 0 | gnutls_datum_t context; |
105 | | |
106 | | /* verify whether the context matches */ |
107 | 0 | ret = _gnutls_buffer_pop_datum_prefix8(&buf, &context); |
108 | 0 | if (ret < 0) { |
109 | 0 | gnutls_assert(); |
110 | 0 | goto cleanup; |
111 | 0 | } |
112 | | |
113 | 0 | if (context.size != |
114 | 0 | session->internals.post_handshake_cr_context.size || |
115 | 0 | !memeq(context.data, |
116 | 0 | session->internals.post_handshake_cr_context.data, |
117 | 0 | context.size)) { |
118 | 0 | ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; |
119 | 0 | gnutls_assert(); |
120 | 0 | goto cleanup; |
121 | 0 | } |
122 | 4.17k | } else { |
123 | 4.17k | if (buf.data[0] != 0) { |
124 | | /* The context field must be empty during handshake */ |
125 | 23 | gnutls_assert(); |
126 | 23 | ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER; |
127 | 23 | goto cleanup; |
128 | 23 | } |
129 | | |
130 | | /* buf.length is positive */ |
131 | 4.15k | buf.data++; |
132 | 4.15k | buf.length--; |
133 | 4.15k | } |
134 | | |
135 | 4.15k | _gnutls_handshake_log("HSK[%p]: parsing certificate message\n", |
136 | 4.15k | session); |
137 | | |
138 | 4.15k | ret = parse_cert_list(session, buf.data, buf.length); |
139 | 4.15k | if (ret < 0) { |
140 | 285 | if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND) { |
141 | 3 | if (optional) |
142 | 0 | ret = 0; |
143 | 3 | else if (session->security_parameters.entity == |
144 | 3 | GNUTLS_SERVER) |
145 | 0 | ret = GNUTLS_E_CERTIFICATE_REQUIRED; |
146 | 3 | } |
147 | 285 | gnutls_assert(); |
148 | 285 | goto cleanup; |
149 | 285 | } |
150 | | |
151 | 3.86k | session->internals.hsk_flags |= HSK_CRT_VRFY_EXPECTED; |
152 | | |
153 | 3.86k | ret = 0; |
154 | 4.18k | cleanup: |
155 | | |
156 | 4.18k | _gnutls_buffer_clear(&buf); |
157 | 4.18k | return ret; |
158 | 3.86k | } |
159 | | |
160 | | struct ocsp_req_ctx_st { |
161 | | gnutls_pcert_st *pcert; |
162 | | unsigned cert_index; |
163 | | gnutls_session_t session; |
164 | | gnutls_certificate_credentials_t cred; |
165 | | }; |
166 | | |
167 | | static int append_status_request(void *_ctx, gnutls_buffer_st *buf) |
168 | 0 | { |
169 | 0 | struct ocsp_req_ctx_st *ctx = _ctx; |
170 | 0 | gnutls_session_t session = ctx->session; |
171 | 0 | int ret; |
172 | 0 | gnutls_datum_t resp; |
173 | 0 | unsigned free_resp = 0; |
174 | |
|
175 | 0 | assert(session->internals.selected_ocsp_func != NULL || |
176 | 0 | session->internals.selected_ocsp_length != 0); |
177 | | |
178 | | /* The global ocsp callback function can only be used to return |
179 | | * a single certificate request */ |
180 | 0 | if (session->internals.selected_ocsp_length == 1 && |
181 | 0 | ctx->cert_index != 0) |
182 | 0 | return 0; |
183 | | |
184 | 0 | if (session->internals.selected_ocsp_length > 0) { |
185 | 0 | if (ctx->cert_index < session->internals.selected_ocsp_length) { |
186 | 0 | if ((session->internals.selected_ocsp[ctx->cert_index] |
187 | 0 | .exptime != 0 && |
188 | 0 | gnutls_time(NULL) >= |
189 | 0 | session->internals |
190 | 0 | .selected_ocsp[ctx->cert_index] |
191 | 0 | .exptime) || |
192 | 0 | session->internals.selected_ocsp[ctx->cert_index] |
193 | 0 | .response.data == NULL) { |
194 | 0 | return 0; |
195 | 0 | } |
196 | | |
197 | 0 | resp.data = session->internals |
198 | 0 | .selected_ocsp[ctx->cert_index] |
199 | 0 | .response.data; |
200 | 0 | resp.size = session->internals |
201 | 0 | .selected_ocsp[ctx->cert_index] |
202 | 0 | .response.size; |
203 | 0 | ret = 0; |
204 | 0 | } else { |
205 | 0 | return 0; |
206 | 0 | } |
207 | 0 | } else if (session->internals.selected_ocsp_func) { |
208 | 0 | if (ctx->cert_index == 0) { |
209 | 0 | ret = session->internals.selected_ocsp_func( |
210 | 0 | session, |
211 | 0 | session->internals.selected_ocsp_func_ptr, |
212 | 0 | &resp); |
213 | 0 | free_resp = 1; |
214 | 0 | } else { |
215 | 0 | return 0; |
216 | 0 | } |
217 | 0 | } else |
218 | 0 | return 0; |
219 | | |
220 | 0 | if (ret == GNUTLS_E_NO_CERTIFICATE_STATUS || resp.data == NULL) { |
221 | 0 | return 0; |
222 | 0 | } else if (ret < 0) { |
223 | 0 | return gnutls_assert_val(ret); |
224 | 0 | } |
225 | | |
226 | 0 | ret = _gnutls_buffer_append_data(buf, "\x01", 1); |
227 | 0 | if (ret < 0) { |
228 | 0 | gnutls_assert(); |
229 | 0 | goto cleanup; |
230 | 0 | } |
231 | | |
232 | 0 | ret = _gnutls_buffer_append_data_prefix24(buf, resp.data, resp.size); |
233 | 0 | if (ret < 0) { |
234 | 0 | gnutls_assert(); |
235 | 0 | goto cleanup; |
236 | 0 | } |
237 | | |
238 | 0 | ret = 0; |
239 | 0 | cleanup: |
240 | 0 | if (free_resp) |
241 | 0 | gnutls_free(resp.data); |
242 | 0 | return ret; |
243 | 0 | } |
244 | | |
245 | | int _gnutls13_send_certificate(gnutls_session_t session, unsigned again) |
246 | 390 | { |
247 | 390 | int ret, compress_cert; |
248 | 390 | gnutls_pcert_st *apr_cert_list = NULL; |
249 | 390 | gnutls_privkey_t apr_pkey = NULL; |
250 | 390 | int apr_cert_list_length = 0; |
251 | 390 | mbuffer_st *bufel = NULL; |
252 | 390 | gnutls_buffer_st buf; |
253 | 390 | unsigned pos_mark, ext_pos_mark, cert_pos_mark; |
254 | 390 | unsigned i; |
255 | 390 | struct ocsp_req_ctx_st ctx; |
256 | 390 | gnutls_certificate_credentials_t cred; |
257 | 390 | gnutls_compression_method_t comp_method; |
258 | 390 | gnutls_handshake_description_t h_type; |
259 | | |
260 | 390 | comp_method = gnutls_compress_certificate_get_selected_method(session); |
261 | 390 | compress_cert = comp_method != GNUTLS_COMP_UNKNOWN; |
262 | 390 | h_type = compress_cert ? GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT : |
263 | 390 | GNUTLS_HANDSHAKE_CERTIFICATE_PKT; |
264 | | |
265 | 390 | if (again == 0) { |
266 | 390 | if (!session->internals.initial_negotiation_completed && |
267 | 390 | session->internals.hsk_flags & HSK_PSK_SELECTED) |
268 | 0 | return 0; |
269 | | |
270 | 390 | if (session->security_parameters.entity == GNUTLS_SERVER && |
271 | 390 | session->internals.resumed) |
272 | 0 | return 0; |
273 | | |
274 | 390 | cred = (gnutls_certificate_credentials_t)_gnutls_get_cred( |
275 | 390 | session, GNUTLS_CRD_CERTIFICATE); |
276 | 390 | if (cred == NULL) { |
277 | 0 | gnutls_assert(); |
278 | 0 | return GNUTLS_E_INSUFFICIENT_CREDENTIALS; |
279 | 0 | } |
280 | | |
281 | 390 | if (session->security_parameters.entity == GNUTLS_CLIENT && |
282 | 0 | !(session->internals.hsk_flags & HSK_CRT_ASKED)) { |
283 | 0 | return 0; |
284 | 0 | } |
285 | | |
286 | 390 | ret = _gnutls_get_selected_cert(session, &apr_cert_list, |
287 | 390 | &apr_cert_list_length, |
288 | 390 | &apr_pkey); |
289 | 390 | if (ret < 0) |
290 | 0 | return gnutls_assert_val(ret); |
291 | | |
292 | 390 | ret = _gnutls_buffer_init_handshake_mbuffer(&buf); |
293 | 390 | if (ret < 0) |
294 | 0 | return gnutls_assert_val(ret); |
295 | | |
296 | 390 | cert_pos_mark = buf.length; |
297 | | |
298 | 390 | if (session->security_parameters.entity == GNUTLS_CLIENT) { |
299 | 0 | ret = _gnutls_buffer_append_data_prefix8( |
300 | 0 | &buf, |
301 | 0 | session->internals.post_handshake_cr_context |
302 | 0 | .data, |
303 | 0 | session->internals.post_handshake_cr_context |
304 | 0 | .size); |
305 | 0 | if (ret < 0) { |
306 | 0 | gnutls_assert(); |
307 | 0 | goto cleanup; |
308 | 0 | } |
309 | |
|
310 | 390 | } else { |
311 | 390 | ret = _gnutls_buffer_append_uint8(&buf, 0); |
312 | 390 | if (ret < 0) { |
313 | 0 | gnutls_assert(); |
314 | 0 | goto cleanup; |
315 | 0 | } |
316 | 390 | } |
317 | | |
318 | | /* mark total size */ |
319 | 390 | pos_mark = buf.length; |
320 | 390 | ret = _gnutls_buffer_append_uint24(&buf, 0); |
321 | 390 | if (ret < 0) { |
322 | 0 | gnutls_assert(); |
323 | 0 | goto cleanup; |
324 | 0 | } |
325 | | |
326 | 780 | for (i = 0; i < (unsigned)apr_cert_list_length; i++) { |
327 | 390 | ret = _gnutls_buffer_append_data_prefix24( |
328 | 390 | &buf, apr_cert_list[i].cert.data, |
329 | 390 | apr_cert_list[i].cert.size); |
330 | 390 | if (ret < 0) { |
331 | 0 | gnutls_assert(); |
332 | 0 | goto cleanup; |
333 | 0 | } |
334 | 390 | #ifdef ENABLE_OCSP |
335 | 390 | if ((session->internals.selected_ocsp_length > 0 || |
336 | 390 | session->internals.selected_ocsp_func) && |
337 | 0 | (((session->internals.hsk_flags & |
338 | 0 | HSK_OCSP_REQUESTED) && |
339 | 0 | IS_SERVER(session)) || |
340 | 0 | ((session->internals.hsk_flags & |
341 | 0 | HSK_CLIENT_OCSP_REQUESTED) && |
342 | 0 | !IS_SERVER(session)))) { |
343 | | /* append status response if available */ |
344 | 0 | ret = _gnutls_extv_append_init(&buf); |
345 | 0 | if (ret < 0) { |
346 | 0 | gnutls_assert(); |
347 | 0 | goto cleanup; |
348 | 0 | } |
349 | 0 | ext_pos_mark = ret; |
350 | |
|
351 | 0 | ctx.pcert = &apr_cert_list[i]; |
352 | 0 | ctx.cert_index = i; |
353 | 0 | ctx.session = session; |
354 | 0 | ctx.cred = cred; |
355 | 0 | ret = _gnutls_extv_append( |
356 | 0 | &buf, STATUS_REQUEST_TLS_ID, &ctx, |
357 | 0 | append_status_request); |
358 | 0 | if (ret < 0) { |
359 | 0 | gnutls_assert(); |
360 | 0 | goto cleanup; |
361 | 0 | } |
362 | | |
363 | 0 | ret = _gnutls_extv_append_final( |
364 | 0 | &buf, ext_pos_mark, 0); |
365 | 0 | if (ret < 0) { |
366 | 0 | gnutls_assert(); |
367 | 0 | goto cleanup; |
368 | 0 | } |
369 | 0 | } else |
370 | 390 | #endif |
371 | 390 | { |
372 | 390 | ret = _gnutls_buffer_append_uint16(&buf, 0); |
373 | 390 | if (ret < 0) { |
374 | 0 | gnutls_assert(); |
375 | 0 | goto cleanup; |
376 | 0 | } |
377 | 390 | } |
378 | 390 | } |
379 | | |
380 | 390 | _gnutls_write_uint24(buf.length - pos_mark - 3, |
381 | 390 | &buf.data[pos_mark]); |
382 | | |
383 | 390 | if (compress_cert) { |
384 | 0 | ret = compress_certificate(&buf, cert_pos_mark, |
385 | 0 | comp_method); |
386 | 0 | if (ret < 0) { |
387 | 0 | gnutls_assert(); |
388 | 0 | goto cleanup; |
389 | 0 | } |
390 | 0 | } |
391 | | |
392 | 390 | bufel = _gnutls_buffer_to_mbuffer(&buf); |
393 | 390 | } |
394 | | |
395 | 390 | return _gnutls_send_handshake(session, bufel, h_type); |
396 | | |
397 | 0 | cleanup: |
398 | 0 | _gnutls_buffer_clear(&buf); |
399 | 0 | return ret; |
400 | 390 | } |
401 | | |
402 | | typedef struct crt_cert_ctx_st { |
403 | | gnutls_session_t session; |
404 | | gnutls_datum_t *ocsp; |
405 | | unsigned idx; |
406 | | } crt_cert_ctx_st; |
407 | | |
408 | | static int parse_cert_extension(void *_ctx, unsigned tls_id, |
409 | | const uint8_t *data, unsigned data_size) |
410 | 159 | { |
411 | 159 | crt_cert_ctx_st *ctx = _ctx; |
412 | 159 | gnutls_session_t session = ctx->session; |
413 | 159 | int ret; |
414 | | |
415 | 159 | if (tls_id == STATUS_REQUEST_TLS_ID) { |
416 | 118 | #ifdef ENABLE_OCSP |
417 | 118 | if (!_gnutls_hello_ext_is_present(session, |
418 | 118 | ext_mod_status_request.gid)) { |
419 | 0 | gnutls_assert(); |
420 | 0 | goto unexpected; |
421 | 0 | } |
422 | | |
423 | 118 | _gnutls_handshake_log("Found OCSP response on cert %d\n", |
424 | 118 | ctx->idx); |
425 | | |
426 | 118 | ret = _gnutls_parse_ocsp_response(session, data, data_size, |
427 | 118 | ctx->ocsp); |
428 | 118 | if (ret < 0) |
429 | 25 | return gnutls_assert_val(ret); |
430 | 118 | #endif |
431 | 118 | } else { |
432 | 41 | goto unexpected; |
433 | 41 | } |
434 | | |
435 | 93 | return 0; |
436 | | |
437 | 41 | unexpected: |
438 | 41 | _gnutls_debug_log("received unexpected certificate extension (%d)\n", |
439 | 41 | (int)tls_id); |
440 | 41 | return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION); |
441 | 159 | } |
442 | | |
443 | | static int parse_cert_list(gnutls_session_t session, uint8_t *data, |
444 | | size_t data_size) |
445 | 4.15k | { |
446 | 4.15k | int ret; |
447 | 4.15k | size_t len; |
448 | 4.15k | uint8_t *p = data; |
449 | 4.15k | cert_auth_info_t info; |
450 | 4.15k | gnutls_certificate_credentials_t cred; |
451 | 4.15k | size_t size; |
452 | 4.15k | int i; |
453 | 4.15k | unsigned npeer_certs, npeer_ocsp, j; |
454 | 4.15k | crt_cert_ctx_st ctx; |
455 | 4.15k | gnutls_datum_t *peer_certs = NULL; |
456 | 4.15k | gnutls_datum_t *peer_ocsp = NULL; |
457 | 4.15k | unsigned nentries = 0; |
458 | | |
459 | 4.15k | cred = (gnutls_certificate_credentials_t)_gnutls_get_cred( |
460 | 4.15k | session, GNUTLS_CRD_CERTIFICATE); |
461 | 4.15k | if (cred == NULL) { |
462 | 7 | gnutls_assert(); |
463 | 7 | return GNUTLS_E_INSUFFICIENT_CREDENTIALS; |
464 | 7 | } |
465 | | |
466 | 4.14k | if ((ret = _gnutls_auth_info_init(session, GNUTLS_CRD_CERTIFICATE, |
467 | 4.14k | sizeof(cert_auth_info_st), 1)) < 0) { |
468 | 0 | gnutls_assert(); |
469 | 0 | return ret; |
470 | 0 | } |
471 | | |
472 | 4.14k | if (data == NULL || data_size == 0) { |
473 | | /* no certificate was sent */ |
474 | 4 | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); |
475 | 4 | } |
476 | | |
477 | 4.14k | info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE); |
478 | 4.14k | if (info == NULL) |
479 | 0 | return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); |
480 | | |
481 | 4.14k | DECR_LEN(data_size, 3); |
482 | 4.13k | size = _gnutls_read_uint24(p); |
483 | 4.13k | p += 3; |
484 | | |
485 | 4.13k | if (size != data_size) |
486 | 72 | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); |
487 | | |
488 | 4.06k | if (size == 0) |
489 | 3 | return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND); |
490 | | |
491 | 4.06k | i = data_size; |
492 | | |
493 | 8.54k | while (i > 0) { |
494 | 4.58k | DECR_LEN(data_size, 3); |
495 | 4.58k | len = _gnutls_read_uint24(p); |
496 | 4.58k | if (len == 0) |
497 | 7 | return gnutls_assert_val( |
498 | 4.58k | GNUTLS_E_UNEXPECTED_PACKET_LENGTH); |
499 | | |
500 | 4.57k | DECR_LEN(data_size, len); |
501 | 4.50k | p += len + 3; |
502 | 4.50k | i -= len + 3; |
503 | | |
504 | 4.50k | DECR_LEN(data_size, 2); |
505 | 4.50k | len = _gnutls_read_uint16(p); |
506 | 4.50k | DECR_LEN(data_size, len); |
507 | | |
508 | 4.48k | i -= len + 2; |
509 | 4.48k | p += len + 2; |
510 | | |
511 | 4.48k | nentries++; |
512 | 4.48k | } |
513 | | |
514 | 3.95k | if (data_size != 0) |
515 | 0 | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); |
516 | | |
517 | | /* this is unnecessary - keeping to avoid a regression due to a re-org |
518 | | * of the loop above */ |
519 | 3.95k | if (nentries == 0) |
520 | 0 | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); |
521 | | |
522 | 3.95k | npeer_ocsp = 0; |
523 | 3.95k | npeer_certs = 0; |
524 | | |
525 | | /* Ok we now allocate the memory to hold the |
526 | | * certificate list |
527 | | */ |
528 | 3.95k | peer_certs = gnutls_calloc(nentries, sizeof(gnutls_datum_t)); |
529 | 3.95k | if (peer_certs == NULL) |
530 | 0 | return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); |
531 | | |
532 | 3.95k | peer_ocsp = gnutls_calloc(nentries, sizeof(gnutls_datum_t)); |
533 | 3.95k | if (peer_ocsp == NULL) { |
534 | 0 | ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); |
535 | 0 | goto cleanup; |
536 | 0 | } |
537 | | |
538 | 3.95k | p = data + 3; |
539 | | |
540 | | /* Now we start parsing the list (again). |
541 | | * We don't use DECR_LEN since the list has |
542 | | * been parsed before. |
543 | | */ |
544 | | |
545 | 3.95k | ctx.session = session; |
546 | | |
547 | 8.19k | for (j = 0; j < nentries; j++) { |
548 | 4.32k | len = _gnutls_read_uint24(p); |
549 | 4.32k | p += 3; |
550 | | |
551 | 4.32k | ret = _gnutls_set_datum(&peer_certs[j], p, len); |
552 | 4.32k | if (ret < 0) { |
553 | 0 | gnutls_assert(); |
554 | 0 | ret = GNUTLS_E_CERTIFICATE_ERROR; |
555 | 0 | goto cleanup; |
556 | 0 | } |
557 | 4.32k | npeer_certs++; |
558 | | |
559 | 4.32k | p += len; |
560 | | |
561 | 4.32k | len = _gnutls_read_uint16(p); |
562 | | |
563 | 4.32k | ctx.ocsp = &peer_ocsp[j]; |
564 | 4.32k | ctx.idx = j; |
565 | | |
566 | 4.32k | ret = _gnutls_extv_parse(&ctx, parse_cert_extension, p, |
567 | 4.32k | len + 2); |
568 | 4.32k | if (ret < 0) { |
569 | 92 | gnutls_assert(); |
570 | 92 | goto cleanup; |
571 | 92 | } |
572 | | |
573 | 4.23k | p += len + 2; |
574 | 4.23k | npeer_ocsp++; |
575 | 4.23k | } |
576 | | |
577 | | /* The OCSP entries match the certificate entries, although |
578 | | * the contents of each OCSP entry may be NULL. |
579 | | */ |
580 | 3.86k | for (j = 0; j < info->ncerts; j++) |
581 | 0 | gnutls_free(info->raw_certificate_list[j].data); |
582 | 3.86k | gnutls_free(info->raw_certificate_list); |
583 | | |
584 | 3.86k | for (j = 0; j < info->nocsp; j++) |
585 | 0 | gnutls_free(info->raw_ocsp_list[j].data); |
586 | 3.86k | gnutls_free(info->raw_ocsp_list); |
587 | | |
588 | 3.86k | info->raw_certificate_list = peer_certs; |
589 | 3.86k | info->ncerts = npeer_certs; |
590 | | |
591 | 3.86k | info->raw_ocsp_list = peer_ocsp; |
592 | 3.86k | info->nocsp = npeer_ocsp; |
593 | | |
594 | 3.86k | return 0; |
595 | | |
596 | 92 | cleanup: |
597 | 360 | for (j = 0; j < npeer_certs; j++) |
598 | 268 | gnutls_free(peer_certs[j].data); |
599 | | |
600 | 268 | for (j = 0; j < npeer_ocsp; j++) |
601 | 176 | gnutls_free(peer_ocsp[j].data); |
602 | 92 | gnutls_free(peer_certs); |
603 | 92 | gnutls_free(peer_ocsp); |
604 | 92 | return ret; |
605 | 3.95k | } |
606 | | |
607 | | static int compress_certificate(gnutls_buffer_st *buf, unsigned cert_pos_mark, |
608 | | gnutls_compression_method_t comp_method) |
609 | 0 | { |
610 | 0 | int ret, method_num; |
611 | 0 | size_t comp_bound; |
612 | 0 | gnutls_datum_t plain, comp = { NULL, 0 }; |
613 | |
|
614 | 0 | method_num = _gnutls_compress_certificate_method2num(comp_method); |
615 | 0 | if (method_num == GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER) |
616 | 0 | return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); |
617 | | |
618 | 0 | plain.data = buf->data + cert_pos_mark; |
619 | 0 | plain.size = buf->length - cert_pos_mark; |
620 | |
|
621 | 0 | comp_bound = _gnutls_compress_bound(comp_method, plain.size); |
622 | 0 | if (comp_bound == 0) |
623 | 0 | return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); |
624 | 0 | comp.data = gnutls_malloc(comp_bound); |
625 | 0 | if (comp.data == NULL) |
626 | 0 | return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); |
627 | 0 | ret = _gnutls_compress(comp_method, comp.data, comp_bound, plain.data, |
628 | 0 | plain.size); |
629 | 0 | if (ret < 0) { |
630 | 0 | gnutls_assert(); |
631 | 0 | goto cleanup; |
632 | 0 | } |
633 | 0 | comp.size = ret; |
634 | |
|
635 | 0 | buf->length = cert_pos_mark; |
636 | 0 | ret = _gnutls_buffer_append_uint16(buf, method_num); |
637 | 0 | if (ret < 0) { |
638 | 0 | gnutls_assert(); |
639 | 0 | goto cleanup; |
640 | 0 | } |
641 | 0 | ret = _gnutls_buffer_append_uint24(buf, plain.size); |
642 | 0 | if (ret < 0) { |
643 | 0 | gnutls_assert(); |
644 | 0 | goto cleanup; |
645 | 0 | } |
646 | 0 | ret = _gnutls_buffer_append_data_prefix24(buf, comp.data, comp.size); |
647 | 0 | if (ret < 0) { |
648 | 0 | gnutls_assert(); |
649 | 0 | goto cleanup; |
650 | 0 | } |
651 | | |
652 | 0 | cleanup: |
653 | 0 | gnutls_free(comp.data); |
654 | 0 | return ret; |
655 | 0 | } |
656 | | |
657 | | static int decompress_certificate(gnutls_session_t session, |
658 | | gnutls_buffer_st *buf) |
659 | 0 | { |
660 | 0 | int ret; |
661 | 0 | uint16_t method_num; |
662 | 0 | uint32_t plain_exp_len; |
663 | 0 | gnutls_datum_t comp, plain = { NULL, 0 }; |
664 | 0 | gnutls_compression_method_t comp_method; |
665 | |
|
666 | 0 | ret = _gnutls_buffer_pop_uint16(buf, &method_num); |
667 | 0 | if (ret < 0) |
668 | 0 | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); |
669 | 0 | comp_method = _gnutls_compress_certificate_num2method(method_num); |
670 | |
|
671 | 0 | if (!_gnutls_compress_certificate_is_method_enabled(session, |
672 | 0 | comp_method)) |
673 | 0 | return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); |
674 | | |
675 | 0 | ret = _gnutls_buffer_pop_uint24(buf, &plain_exp_len); |
676 | 0 | if (ret < 0) |
677 | 0 | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); |
678 | | |
679 | 0 | ret = _gnutls_buffer_pop_datum_prefix24(buf, &comp); |
680 | 0 | if (ret < 0 || buf->length > 0 || comp.size == 0) |
681 | 0 | return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); |
682 | | |
683 | 0 | plain.data = gnutls_malloc(plain_exp_len); |
684 | 0 | if (plain.data == NULL) |
685 | 0 | return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); |
686 | 0 | ret = _gnutls_decompress(comp_method, plain.data, plain_exp_len, |
687 | 0 | comp.data, comp.size); |
688 | 0 | if (ret < 0) { |
689 | 0 | ret = gnutls_assert_val(GNUTLS_E_CERTIFICATE_ERROR); |
690 | 0 | goto cleanup; |
691 | 0 | } |
692 | 0 | plain.size = ret; |
693 | |
|
694 | 0 | if (plain.size != plain_exp_len) { |
695 | 0 | ret = gnutls_assert_val(GNUTLS_E_CERTIFICATE_ERROR); |
696 | 0 | goto cleanup; |
697 | 0 | } |
698 | | |
699 | 0 | _gnutls_buffer_clear(buf); |
700 | 0 | ret = _gnutls_buffer_append_data(buf, plain.data, plain.size); |
701 | 0 | if (ret < 0) { |
702 | 0 | ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); |
703 | 0 | goto cleanup; |
704 | 0 | } |
705 | | |
706 | 0 | cleanup: |
707 | | gnutls_free(plain.data); |
708 | 0 | return ret; |
709 | 0 | } |