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