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_request.h"
28 : #include "ext/signature.h"
29 : #include "ext/status_request.h"
30 : #include "mbuffers.h"
31 : #include "algorithms.h"
32 : #include "auth/cert.h"
33 :
34 : /* for tlist dereference */
35 : #include "x509/verify-high.h"
36 :
37 : #define EXTID_CERTIFICATE_AUTHORITIES 47
38 :
39 : typedef struct crt_req_ctx_st {
40 : gnutls_session_t session;
41 : unsigned got_sig_algo;
42 : gnutls_pk_algorithm_t pk_algos[MAX_ALGOS];
43 : unsigned pk_algos_length;
44 : const uint8_t *rdn; /* pointer inside the message buffer */
45 : unsigned rdn_size;
46 : } crt_req_ctx_st;
47 :
48 2435 : static unsigned is_algo_in_list(gnutls_pk_algorithm_t algo, gnutls_pk_algorithm_t *list, unsigned list_size)
49 : {
50 2435 : unsigned j;
51 :
52 5707 : for (j=0;j<list_size;j++) {
53 4946 : if (list[j] == algo)
54 : return 1;
55 : }
56 : return 0;
57 : }
58 :
59 : static
60 324 : int parse_cert_extension(void *_ctx, unsigned tls_id, const uint8_t *data, unsigned data_size)
61 : {
62 324 : crt_req_ctx_st *ctx = _ctx;
63 324 : gnutls_session_t session = ctx->session;
64 324 : unsigned v;
65 324 : int ret;
66 :
67 : /* Decide which certificate to use if the signature algorithms extension
68 : * is present.
69 : */
70 324 : if (tls_id == ext_mod_sig.tls_id) {
71 153 : const version_entry_st *ver = get_version(session);
72 153 : const gnutls_sign_entry_st *se;
73 : /* signature algorithms; let's use it to decide the certificate to use */
74 153 : unsigned i;
75 :
76 153 : if (ctx->got_sig_algo)
77 0 : return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
78 :
79 153 : ctx->got_sig_algo = 1;
80 :
81 153 : if (data_size < 2)
82 0 : return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
83 :
84 153 : v = _gnutls_read_uint16(data);
85 153 : if (v != data_size-2)
86 0 : return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
87 :
88 153 : data += 2;
89 153 : data_size -= 2;
90 :
91 153 : ret = _gnutls_sign_algorithm_parse_data(session, data, data_size);
92 153 : if (ret < 0)
93 0 : return gnutls_assert_val(ret);
94 :
95 : /* The APIs to retrieve a client certificate accept the public
96 : * key algorithms instead of signatures. Get the public key algorithms
97 : * from the signatures.
98 : */
99 2598 : for (i=0;i<(unsigned)data_size;i+=2) {
100 2445 : se = _gnutls_tls_aid_to_sign_entry(data[i], data[i+1], ver);
101 2445 : if (se == NULL)
102 10 : continue;
103 :
104 2435 : if (ctx->pk_algos_length >= sizeof(ctx->pk_algos)/sizeof(ctx->pk_algos[0]))
105 : break;
106 :
107 2435 : if (is_algo_in_list(se->pk, ctx->pk_algos, ctx->pk_algos_length))
108 1674 : continue;
109 :
110 761 : ctx->pk_algos[ctx->pk_algos_length++] = se->pk;
111 : }
112 : #ifdef ENABLE_OCSP
113 171 : } else if (tls_id == ext_mod_status_request.tls_id) {
114 148 : if (data_size != 0)
115 0 : return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
116 :
117 : /* we are now allowed to send OCSP staples */
118 148 : session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED;
119 : #endif
120 23 : } else if (tls_id == EXTID_CERTIFICATE_AUTHORITIES) {
121 23 : if (data_size < 3) {
122 0 : return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
123 : }
124 :
125 23 : v = _gnutls_read_uint16(data);
126 23 : if (v != data_size-2)
127 0 : return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
128 :
129 23 : ctx->rdn = data+2;
130 23 : ctx->rdn_size = v;
131 : }
132 :
133 : return 0;
134 : }
135 :
136 153 : int _gnutls13_recv_certificate_request_int(gnutls_session_t session, gnutls_buffer_st *buf)
137 : {
138 153 : int ret;
139 153 : crt_req_ctx_st ctx;
140 153 : gnutls_pcert_st *apr_cert_list;
141 153 : gnutls_privkey_t apr_pkey;
142 153 : int apr_cert_list_length;
143 :
144 153 : _gnutls_handshake_log("HSK[%p]: parsing certificate request\n", session);
145 :
146 153 : if (unlikely(session->security_parameters.entity == GNUTLS_SERVER))
147 0 : return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
148 :
149 : /* if initial negotiation is complete, this is a post-handshake auth */
150 153 : if (!session->internals.initial_negotiation_completed) {
151 151 : if (buf->data[0] != 0) {
152 : /* The context field must be empty during handshake */
153 0 : return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
154 : }
155 :
156 : /* buf->length is positive */
157 151 : buf->data++;
158 151 : buf->length--;
159 : } else {
160 2 : gnutls_datum_t context;
161 :
162 2 : ret = _gnutls_buffer_pop_datum_prefix8(buf, &context);
163 2 : if (ret < 0)
164 0 : return gnutls_assert_val(ret);
165 :
166 2 : gnutls_free(session->internals.post_handshake_cr_context.data);
167 4 : ret = _gnutls_set_datum(&session->internals.post_handshake_cr_context,
168 2 : context.data, context.size);
169 2 : if (ret < 0)
170 0 : return gnutls_assert_val(ret);
171 : }
172 :
173 153 : memset(&ctx, 0, sizeof(ctx));
174 153 : ctx.session = session;
175 :
176 153 : ret = _gnutls_extv_parse(&ctx, parse_cert_extension, buf->data, buf->length);
177 153 : if (ret < 0)
178 0 : return gnutls_assert_val(ret);
179 :
180 : /* The "signature_algorithms" extension MUST be specified */
181 153 : if (!ctx.got_sig_algo)
182 0 : return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
183 :
184 153 : session->internals.hsk_flags |= HSK_CRT_ASKED;
185 :
186 306 : ret = _gnutls_select_client_cert(session, ctx.rdn, ctx.rdn_size,
187 153 : ctx.pk_algos, ctx.pk_algos_length);
188 153 : if (ret < 0)
189 0 : return gnutls_assert_val(ret);
190 :
191 153 : ret = _gnutls_get_selected_cert(session, &apr_cert_list,
192 : &apr_cert_list_length, &apr_pkey);
193 153 : if (ret < 0)
194 0 : return gnutls_assert_val(ret);
195 :
196 153 : if (apr_cert_list_length > 0) {
197 49 : gnutls_sign_algorithm_t algo;
198 :
199 49 : algo = _gnutls_session_get_sign_algo(session, &apr_cert_list[0], apr_pkey, 0, GNUTLS_KX_UNKNOWN);
200 49 : if (algo == GNUTLS_SIGN_UNKNOWN) {
201 1 : _gnutls_handshake_log("HSK[%p]: rejecting client auth because of no suitable signature algorithm\n", session);
202 1 : _gnutls_selected_certs_deinit(session);
203 1 : return gnutls_assert_val(0);
204 : }
205 :
206 48 : gnutls_sign_algorithm_set_client(session, algo);
207 : }
208 :
209 : return 0;
210 : }
211 :
212 652 : int _gnutls13_recv_certificate_request(gnutls_session_t session)
213 : {
214 652 : int ret;
215 652 : gnutls_buffer_st buf;
216 :
217 652 : if (!session->internals.initial_negotiation_completed &&
218 652 : session->internals.hsk_flags & HSK_PSK_SELECTED)
219 : return 0;
220 :
221 528 : if (unlikely(session->security_parameters.entity != GNUTLS_CLIENT))
222 0 : return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
223 :
224 528 : ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, 1, &buf);
225 528 : if (ret < 0)
226 37 : return gnutls_assert_val(ret);
227 :
228 : /* if not received */
229 491 : if (buf.length == 0) {
230 340 : _gnutls_buffer_clear(&buf);
231 340 : return 0;
232 : }
233 :
234 151 : ret = _gnutls13_recv_certificate_request_int(session, &buf);
235 :
236 151 : _gnutls_buffer_clear(&buf);
237 151 : return ret;
238 : }
239 :
240 : static
241 249 : int write_certificate_authorities(void *ctx, gnutls_buffer_st *buf)
242 : {
243 249 : gnutls_session_t session = ctx;
244 249 : gnutls_certificate_credentials_t cred;
245 :
246 249 : if (session->internals.ignore_rdn_sequence != 0)
247 : return 0;
248 :
249 249 : cred = (gnutls_certificate_credentials_t)
250 249 : _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
251 249 : if (cred == NULL) {
252 0 : gnutls_assert();
253 0 : return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
254 : }
255 :
256 249 : if (cred->tlist->x509_rdn_sequence.size == 0)
257 : return 0;
258 :
259 42 : return
260 42 : _gnutls_buffer_append_data_prefix(buf, 16,
261 : cred->
262 42 : tlist->x509_rdn_sequence.
263 : data,
264 : cred->
265 : tlist->x509_rdn_sequence.
266 : size);
267 : }
268 :
269 249 : static int append_empty_ext(void *ctx, gnutls_buffer_st *buf)
270 : {
271 249 : return GNUTLS_E_INT_RET_0;
272 : }
273 :
274 5223 : int _gnutls13_send_certificate_request(gnutls_session_t session, unsigned again)
275 : {
276 5223 : gnutls_certificate_credentials_t cred;
277 5223 : int ret;
278 5223 : mbuffer_st *bufel = NULL;
279 5223 : gnutls_buffer_st buf;
280 5223 : unsigned init_pos;
281 :
282 5223 : if (again == 0) {
283 5223 : unsigned char rnd[12];
284 :
285 5223 : if (!session->internals.initial_negotiation_completed &&
286 5191 : session->internals.hsk_flags & HSK_PSK_SELECTED)
287 4974 : return 0;
288 :
289 3756 : if (session->internals.send_cert_req == 0)
290 : return 0;
291 :
292 249 : cred = (gnutls_certificate_credentials_t)
293 249 : _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
294 249 : if (cred == NULL)
295 0 : return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
296 :
297 498 : ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
298 249 : if (ret < 0)
299 0 : return gnutls_assert_val(ret);
300 :
301 249 : if (session->internals.initial_negotiation_completed) { /* reauth */
302 32 : ret = gnutls_rnd(GNUTLS_RND_NONCE, rnd, sizeof(rnd));
303 32 : if (ret < 0) {
304 0 : gnutls_assert();
305 0 : goto cleanup;
306 : }
307 :
308 32 : gnutls_free(session->internals.post_handshake_cr_context.data);
309 32 : ret = _gnutls_set_datum(&session->internals.post_handshake_cr_context,
310 : rnd, sizeof(rnd));
311 32 : if (ret < 0) {
312 0 : gnutls_assert();
313 0 : goto cleanup;
314 : }
315 :
316 32 : ret = _gnutls_buffer_append_data_prefix(&buf, 8,
317 32 : session->internals.post_handshake_cr_context.data,
318 32 : session->internals.post_handshake_cr_context.size);
319 : } else {
320 217 : ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
321 : }
322 :
323 249 : if (ret < 0) {
324 0 : gnutls_assert();
325 0 : goto cleanup;
326 : }
327 :
328 249 : ret = _gnutls_extv_append_init(&buf);
329 249 : if (ret < 0) {
330 0 : gnutls_assert();
331 0 : goto cleanup;
332 : }
333 249 : init_pos = ret;
334 :
335 249 : ret = _gnutls_extv_append(&buf, ext_mod_sig.tls_id, session,
336 : (extv_append_func)_gnutls_sign_algorithm_write_params);
337 249 : if (ret < 0) {
338 0 : gnutls_assert();
339 0 : goto cleanup;
340 : }
341 :
342 249 : ret = _gnutls_extv_append(&buf, EXTID_CERTIFICATE_AUTHORITIES, session,
343 : write_certificate_authorities);
344 249 : if (ret < 0) {
345 0 : gnutls_assert();
346 0 : goto cleanup;
347 : }
348 :
349 : #ifdef ENABLE_OCSP
350 : /* We always advertise our support for OCSP stapling */
351 249 : ret = _gnutls_extv_append(&buf, ext_mod_status_request.tls_id, session,
352 : append_empty_ext);
353 249 : if (ret < 0) {
354 0 : gnutls_assert();
355 0 : goto cleanup;
356 : }
357 249 : session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED;
358 : #endif
359 :
360 249 : ret = _gnutls_extv_append_final(&buf, init_pos, 0);
361 249 : if (ret < 0) {
362 0 : gnutls_assert();
363 0 : goto cleanup;
364 : }
365 :
366 249 : bufel = _gnutls_buffer_to_mbuffer(&buf);
367 :
368 249 : session->internals.hsk_flags |= HSK_CRT_REQ_SENT;
369 : }
370 :
371 249 : return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST);
372 :
373 0 : cleanup:
374 0 : _gnutls_buffer_clear(&buf);
375 0 : return ret;
376 :
377 : }
378 :
|