Line data Source code
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 "errors.h"
25 : #include "extv.h"
26 : #include "handshake.h"
27 : #include "tls13/certificate.h"
28 : #include "auth/cert.h"
29 : #include "mbuffers.h"
30 : #include "ext/status_request.h"
31 :
32 : static int parse_cert_extension(void *ctx, unsigned tls_id, const uint8_t *data, unsigned data_size);
33 : static int parse_cert_list(gnutls_session_t session, uint8_t * data, size_t data_size);
34 :
35 19715 : int _gnutls13_recv_certificate(gnutls_session_t session)
36 : {
37 19715 : int ret;
38 19715 : gnutls_buffer_st buf;
39 19715 : unsigned optional = 0;
40 :
41 19715 : if (!session->internals.initial_negotiation_completed &&
42 5855 : session->internals.hsk_flags & HSK_PSK_SELECTED)
43 : return 0;
44 :
45 18125 : if (session->security_parameters.entity == GNUTLS_SERVER) {
46 : /* if we didn't request a certificate, there will not be any */
47 17623 : if (session->internals.send_cert_req == 0)
48 : return 0;
49 :
50 14139 : if (session->internals.send_cert_req != GNUTLS_CERT_REQUIRE)
51 14043 : optional = 1;
52 : }
53 :
54 14641 : ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_PKT, 0, &buf);
55 14641 : if (ret < 0) {
56 13951 : if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET && session->internals.send_cert_req)
57 0 : return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND);
58 :
59 13990 : return gnutls_assert_val(ret);
60 : }
61 :
62 690 : if (buf.length == 0) {
63 0 : gnutls_assert();
64 0 : ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
65 0 : goto cleanup;
66 : }
67 :
68 690 : if (session->internals.initial_negotiation_completed &&
69 64 : session->internals.post_handshake_cr_context.size > 0) {
70 32 : gnutls_datum_t context;
71 :
72 : /* verify whether the context matches */
73 32 : ret = _gnutls_buffer_pop_datum_prefix8(&buf, &context);
74 32 : if (ret < 0) {
75 0 : gnutls_assert();
76 0 : goto cleanup;
77 : }
78 :
79 32 : if (context.size != session->internals.post_handshake_cr_context.size ||
80 32 : memcmp(context.data, session->internals.post_handshake_cr_context.data,
81 : context.size) != 0) {
82 0 : ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
83 0 : gnutls_assert();
84 0 : goto cleanup;
85 : }
86 : } else {
87 658 : if (buf.data[0] != 0) {
88 : /* The context field must be empty during handshake */
89 0 : gnutls_assert();
90 0 : ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
91 0 : goto cleanup;
92 : }
93 :
94 : /* buf.length is positive */
95 658 : buf.data++;
96 658 : buf.length--;
97 : }
98 :
99 690 : _gnutls_handshake_log("HSK[%p]: parsing certificate message\n", session);
100 :
101 690 : ret = parse_cert_list(session, buf.data, buf.length);
102 690 : if (ret < 0) {
103 122 : if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND) {
104 122 : if (optional)
105 : ret = 0;
106 20 : else if (session->security_parameters.entity ==
107 : GNUTLS_SERVER)
108 20 : ret = GNUTLS_E_CERTIFICATE_REQUIRED;
109 : }
110 122 : gnutls_assert();
111 122 : goto cleanup;
112 : }
113 :
114 568 : session->internals.hsk_flags |= HSK_CRT_VRFY_EXPECTED;
115 :
116 568 : ret = 0;
117 690 : cleanup:
118 :
119 690 : _gnutls_buffer_clear(&buf);
120 690 : return ret;
121 : }
122 :
123 : struct ocsp_req_ctx_st {
124 : gnutls_pcert_st *pcert;
125 : unsigned cert_index;
126 : gnutls_session_t session;
127 : gnutls_certificate_credentials_t cred;
128 : };
129 :
130 : static
131 45 : int append_status_request(void *_ctx, gnutls_buffer_st *buf)
132 : {
133 45 : struct ocsp_req_ctx_st *ctx = _ctx;
134 45 : gnutls_session_t session = ctx->session;
135 45 : int ret;
136 45 : gnutls_datum_t resp;
137 45 : unsigned free_resp = 0;
138 :
139 45 : assert(session->internals.selected_ocsp_func != NULL ||
140 : session->internals.selected_ocsp_length != 0);
141 :
142 : /* The global ocsp callback function can only be used to return
143 : * a single certificate request */
144 45 : if (session->internals.selected_ocsp_length == 1 && ctx->cert_index != 0)
145 : return 0;
146 :
147 41 : if (session->internals.selected_ocsp_length > 0) {
148 28 : if (ctx->cert_index < session->internals.selected_ocsp_length) {
149 28 : if ((session->internals.selected_ocsp[ctx->cert_index].exptime != 0 &&
150 14 : gnutls_time(0) >= session->internals.selected_ocsp[ctx->cert_index].exptime) ||
151 28 : session->internals.selected_ocsp[ctx->cert_index].response.data == NULL) {
152 0 : return 0;
153 : }
154 :
155 28 : resp.data = session->internals.selected_ocsp[ctx->cert_index].response.data;
156 28 : resp.size = session->internals.selected_ocsp[ctx->cert_index].response.size;
157 28 : ret = 0;
158 : } else {
159 : return 0;
160 : }
161 13 : } else if (session->internals.selected_ocsp_func) {
162 13 : if (ctx->cert_index == 0) {
163 7 : ret = session->internals.selected_ocsp_func(session, session->internals.selected_ocsp_func_ptr, &resp);
164 7 : free_resp = 1;
165 : } else {
166 : return 0;
167 : }
168 : } else
169 : return 0;
170 :
171 35 : if (ret == GNUTLS_E_NO_CERTIFICATE_STATUS || resp.data == 0) {
172 0 : return 0;
173 35 : } else if (ret < 0) {
174 0 : return gnutls_assert_val(ret);
175 : }
176 :
177 35 : ret = _gnutls_buffer_append_data(buf, "\x01", 1);
178 35 : if (ret < 0) {
179 0 : gnutls_assert();
180 0 : goto cleanup;
181 : }
182 :
183 35 : ret = _gnutls_buffer_append_data_prefix(buf, 24, resp.data, resp.size);
184 35 : if (ret < 0) {
185 0 : gnutls_assert();
186 0 : goto cleanup;
187 : }
188 :
189 : ret = 0;
190 35 : cleanup:
191 35 : if (free_resp)
192 7 : gnutls_free(resp.data);
193 : return ret;
194 : }
195 :
196 5786 : int _gnutls13_send_certificate(gnutls_session_t session, unsigned again)
197 : {
198 5786 : int ret;
199 5786 : gnutls_pcert_st *apr_cert_list = NULL;
200 5786 : gnutls_privkey_t apr_pkey = NULL;
201 5786 : int apr_cert_list_length = 0;
202 5786 : mbuffer_st *bufel = NULL;
203 5786 : gnutls_buffer_st buf;
204 5786 : unsigned pos_mark, ext_pos_mark;
205 5786 : unsigned i;
206 5786 : struct ocsp_req_ctx_st ctx;
207 5786 : gnutls_certificate_credentials_t cred;
208 :
209 5786 : if (again == 0) {
210 5786 : if (!session->internals.initial_negotiation_completed &&
211 5784 : session->internals.hsk_flags & HSK_PSK_SELECTED)
212 : return 0;
213 :
214 4195 : if (session->security_parameters.entity == GNUTLS_SERVER &&
215 3724 : session->internals.resumed)
216 : return 0;
217 :
218 4195 : cred = (gnutls_certificate_credentials_t)
219 4195 : _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
220 4195 : if (cred == NULL) {
221 11 : gnutls_assert();
222 11 : return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
223 : }
224 :
225 4184 : if (session->security_parameters.entity == GNUTLS_CLIENT &&
226 471 : !(session->internals.hsk_flags & HSK_CRT_ASKED)) {
227 : return 0;
228 : }
229 :
230 3854 : ret = _gnutls_get_selected_cert(session, &apr_cert_list,
231 : &apr_cert_list_length, &apr_pkey);
232 3854 : if (ret < 0)
233 0 : return gnutls_assert_val(ret);
234 :
235 7708 : ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
236 3854 : if (ret < 0)
237 0 : return gnutls_assert_val(ret);
238 :
239 3854 : if (session->security_parameters.entity == GNUTLS_CLIENT) {
240 282 : ret = _gnutls_buffer_append_data_prefix(&buf, 8,
241 141 : session->internals.post_handshake_cr_context.data,
242 141 : session->internals.post_handshake_cr_context.size);
243 141 : if (ret < 0) {
244 0 : gnutls_assert();
245 0 : goto cleanup;
246 : }
247 :
248 : } else {
249 3713 : ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
250 3713 : if (ret < 0) {
251 0 : gnutls_assert();
252 0 : goto cleanup;
253 : }
254 : }
255 :
256 : /* mark total size */
257 3854 : pos_mark = buf.length;
258 3854 : ret = _gnutls_buffer_append_prefix(&buf, 24, 0);
259 3854 : if (ret < 0) {
260 0 : gnutls_assert();
261 0 : goto cleanup;
262 : }
263 :
264 8001 : for (i=0;i<(unsigned)apr_cert_list_length;i++) {
265 8294 : ret = _gnutls_buffer_append_data_prefix(&buf, 24,
266 4147 : apr_cert_list[i].cert.data,
267 4147 : apr_cert_list[i].cert.size);
268 4147 : if (ret < 0) {
269 0 : gnutls_assert();
270 0 : goto cleanup;
271 : }
272 :
273 : #ifdef ENABLE_OCSP
274 4147 : if ((session->internals.selected_ocsp_length > 0 ||
275 4115 : session->internals.selected_ocsp_func) &&
276 49 : (((session->internals.hsk_flags & HSK_OCSP_REQUESTED) && IS_SERVER(session)) ||
277 7 : ((session->internals.hsk_flags & HSK_CLIENT_OCSP_REQUESTED) && !IS_SERVER(session)))) {
278 : /* append status response if available */
279 45 : ret = _gnutls_extv_append_init(&buf);
280 45 : if (ret < 0) {
281 0 : gnutls_assert();
282 0 : goto cleanup;
283 : }
284 45 : ext_pos_mark = ret;
285 :
286 45 : ctx.pcert = &apr_cert_list[i];
287 45 : ctx.cert_index = i;
288 45 : ctx.session = session;
289 45 : ctx.cred = cred;
290 45 : ret = _gnutls_extv_append(&buf, STATUS_REQUEST_TLS_ID,
291 : &ctx, append_status_request);
292 45 : if (ret < 0) {
293 0 : gnutls_assert();
294 0 : goto cleanup;
295 : }
296 :
297 45 : ret = _gnutls_extv_append_final(&buf, ext_pos_mark, 0);
298 45 : if (ret < 0) {
299 0 : gnutls_assert();
300 0 : goto cleanup;
301 : }
302 : } else
303 : #endif
304 : {
305 4102 : ret = _gnutls_buffer_append_prefix(&buf, 16, 0);
306 4102 : if (ret < 0) {
307 0 : gnutls_assert();
308 0 : goto cleanup;
309 : }
310 : }
311 : }
312 :
313 3854 : _gnutls_write_uint24(buf.length-pos_mark-3, &buf.data[pos_mark]);
314 :
315 3854 : bufel = _gnutls_buffer_to_mbuffer(&buf);
316 : }
317 :
318 3854 : return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_PKT);
319 :
320 0 : cleanup:
321 0 : _gnutls_buffer_clear(&buf);
322 0 : return ret;
323 : }
324 :
325 : typedef struct crt_cert_ctx_st {
326 : gnutls_session_t session;
327 : gnutls_datum_t *ocsp;
328 : unsigned idx;
329 : } crt_cert_ctx_st;
330 :
331 39 : static int parse_cert_extension(void *_ctx, unsigned tls_id, const uint8_t *data, unsigned data_size)
332 : {
333 39 : crt_cert_ctx_st *ctx = _ctx;
334 39 : gnutls_session_t session = ctx->session;
335 39 : int ret;
336 :
337 39 : if (tls_id == STATUS_REQUEST_TLS_ID) {
338 : #ifdef ENABLE_OCSP
339 39 : if (!_gnutls_hello_ext_is_present(session, ext_mod_status_request.gid)) {
340 0 : gnutls_assert();
341 0 : goto unexpected;
342 : }
343 :
344 39 : _gnutls_handshake_log("Found OCSP response on cert %d\n", ctx->idx);
345 :
346 39 : ret = _gnutls_parse_ocsp_response(session, data, data_size, ctx->ocsp);
347 39 : if (ret < 0)
348 0 : return gnutls_assert_val(ret);
349 : #endif
350 : } else {
351 0 : goto unexpected;
352 : }
353 :
354 : return 0;
355 :
356 0 : unexpected:
357 0 : _gnutls_debug_log("received unexpected certificate extension (%d)\n", (int)tls_id);
358 0 : return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
359 : }
360 :
361 : static int
362 690 : parse_cert_list(gnutls_session_t session, uint8_t * data, size_t data_size)
363 : {
364 690 : int ret;
365 690 : size_t len;
366 690 : uint8_t *p = data;
367 690 : cert_auth_info_t info;
368 690 : gnutls_certificate_credentials_t cred;
369 690 : size_t size;
370 690 : int i;
371 690 : unsigned npeer_certs, npeer_ocsp, j;
372 690 : crt_cert_ctx_st ctx;
373 690 : gnutls_datum_t *peer_certs = NULL;
374 690 : gnutls_datum_t *peer_ocsp = NULL;
375 690 : unsigned nentries = 0;
376 :
377 690 : cred = (gnutls_certificate_credentials_t)
378 690 : _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
379 690 : if (cred == NULL) {
380 0 : gnutls_assert();
381 0 : return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
382 : }
383 :
384 1380 : if ((ret =
385 690 : _gnutls_auth_info_init(session, GNUTLS_CRD_CERTIFICATE,
386 : sizeof(cert_auth_info_st), 1)) < 0) {
387 0 : gnutls_assert();
388 0 : return ret;
389 : }
390 :
391 690 : if (data == NULL || data_size == 0) {
392 : /* no certificate was sent */
393 0 : return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
394 : }
395 :
396 690 : info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
397 690 : if (info == NULL)
398 0 : return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
399 :
400 690 : DECR_LEN(data_size, 3);
401 690 : size = _gnutls_read_uint24(p);
402 690 : p += 3;
403 :
404 690 : if (size != data_size)
405 0 : return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
406 :
407 690 : if (size == 0)
408 122 : return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND);
409 :
410 568 : i = data_size;
411 :
412 1404 : while (i > 0) {
413 836 : DECR_LEN(data_size, 3);
414 836 : len = _gnutls_read_uint24(p);
415 836 : if (len == 0)
416 0 : return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
417 :
418 836 : DECR_LEN(data_size, len);
419 836 : p += len + 3;
420 836 : i -= len + 3;
421 :
422 836 : DECR_LEN(data_size, 2);
423 836 : len = _gnutls_read_uint16(p);
424 836 : DECR_LEN(data_size, len);
425 :
426 836 : i -= len + 2;
427 836 : p += len + 2;
428 :
429 836 : nentries++;
430 : }
431 :
432 568 : if (data_size != 0)
433 0 : return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
434 :
435 : /* this is unnecessary - keeping to avoid a regression due to a re-org
436 : * of the loop above */
437 568 : if (nentries == 0)
438 0 : return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
439 :
440 568 : npeer_ocsp = 0;
441 568 : npeer_certs = 0;
442 :
443 : /* Ok we now allocate the memory to hold the
444 : * certificate list
445 : */
446 568 : peer_certs = gnutls_calloc(nentries, sizeof(gnutls_datum_t));
447 568 : if (peer_certs == NULL)
448 0 : return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
449 :
450 568 : peer_ocsp = gnutls_calloc(nentries, sizeof(gnutls_datum_t));
451 568 : if (peer_ocsp == NULL) {
452 0 : ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
453 0 : goto cleanup;
454 : }
455 :
456 568 : p = data+3;
457 :
458 : /* Now we start parsing the list (again).
459 : * We don't use DECR_LEN since the list has
460 : * been parsed before.
461 : */
462 :
463 568 : ctx.session = session;
464 :
465 1404 : for (j = 0; j < nentries; j++) {
466 836 : len = _gnutls_read_uint24(p);
467 836 : p += 3;
468 :
469 836 : ret = _gnutls_set_datum(&peer_certs[j], p, len);
470 836 : if (ret < 0) {
471 0 : gnutls_assert();
472 0 : ret = GNUTLS_E_CERTIFICATE_ERROR;
473 0 : goto cleanup;
474 : }
475 836 : npeer_certs++;
476 :
477 836 : p += len;
478 :
479 836 : len = _gnutls_read_uint16(p);
480 :
481 836 : ctx.ocsp = &peer_ocsp[j];
482 836 : ctx.idx = j;
483 :
484 836 : ret = _gnutls_extv_parse(&ctx, parse_cert_extension, p, len+2);
485 836 : if (ret < 0) {
486 0 : gnutls_assert();
487 0 : goto cleanup;
488 : }
489 :
490 836 : p += len+2;
491 836 : npeer_ocsp++;
492 : }
493 :
494 : /* The OCSP entries match the certificate entries, although
495 : * the contents of each OCSP entry may be NULL.
496 : */
497 592 : for(j=0;j<info->ncerts;j++)
498 24 : gnutls_free(info->raw_certificate_list[j].data);
499 568 : gnutls_free(info->raw_certificate_list);
500 :
501 592 : for(j=0;j<info->nocsp;j++)
502 24 : gnutls_free(info->raw_ocsp_list[j].data);
503 568 : gnutls_free(info->raw_ocsp_list);
504 :
505 :
506 568 : info->raw_certificate_list = peer_certs;
507 568 : info->ncerts = npeer_certs;
508 :
509 568 : info->raw_ocsp_list = peer_ocsp;
510 568 : info->nocsp = npeer_ocsp;
511 :
512 568 : return 0;
513 :
514 0 : cleanup:
515 0 : for(j=0;j<npeer_certs;j++)
516 0 : gnutls_free(peer_certs[j].data);
517 :
518 0 : for(j=0;j<npeer_ocsp;j++)
519 0 : gnutls_free(peer_ocsp[j].data);
520 0 : gnutls_free(peer_certs);
521 0 : gnutls_free(peer_ocsp);
522 0 : return ret;
523 :
524 : }
525 :
|