/src/freeradius-server/src/lib/tls/verify.c
Line | Count | Source |
1 | | /* |
2 | | * This program is free software; you can redistribute it and/or modify |
3 | | * it under the terms of the GNU General Public License as published by |
4 | | * the Free Software Foundation; either version 2 of the License, or |
5 | | * (at your option) any later version. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** |
18 | | * $Id: d14b1103fe9eda9a3fea20448c416204bb2ea887 $ |
19 | | * |
20 | | * @file tls/verify.c |
21 | | * @brief Expose certificate OIDs as attributes, and call validation virtual |
22 | | * server to check cert is valid. |
23 | | * |
24 | | * @copyright 2001 hereUare Communications, Inc. (raghud@hereuare.com) |
25 | | * @copyright 2003 Alan DeKok (aland@freeradius.org) |
26 | | * @copyright 2006-2016 The FreeRADIUS server project |
27 | | */ |
28 | | #ifdef WITH_TLS |
29 | 0 | #define LOG_PREFIX "tls" |
30 | | |
31 | | #include <freeradius-devel/server/exec.h> |
32 | | #include <freeradius-devel/server/pair.h> |
33 | | #include <freeradius-devel/tls/log.h> |
34 | | #include <freeradius-devel/unlang/function.h> |
35 | | #include <freeradius-devel/unlang/subrequest.h> |
36 | | #include <freeradius-devel/util/debug.h> |
37 | | #include <freeradius-devel/util/strerror.h> |
38 | | #include <freeradius-devel/util/syserror.h> |
39 | | |
40 | | #include "attrs.h" |
41 | | #include "base.h" |
42 | | |
43 | | /** Check to see if a verification operation should apply to a certificate |
44 | | * |
45 | | * @param[in] depth starting at 0. |
46 | | * Certificate 0 is the leaf cert (i.e. the client or server cert); |
47 | | * @param[in] untrusted The number of untrusted certificates. |
48 | | * @param[in] mode to check |
49 | | * @return |
50 | | * - true if a given validation check should apply. |
51 | | ** - false if a validation check should not apply. |
52 | | */ |
53 | | static inline CC_HINT(always_inline) |
54 | | bool verify_applies(fr_tls_verify_mode_t mode, int depth, int untrusted) |
55 | 0 | { |
56 | 0 | if (mode == FR_TLS_VERIFY_MODE_ALL) return true; |
57 | 0 | if (mode == FR_TLS_VERIFY_MODE_DISABLED) return false; |
58 | | |
59 | 0 | if ((mode & FR_TLS_VERIFY_MODE_LEAF) && (depth == 0)) return true; |
60 | 0 | if ((mode & FR_TLS_VERIFY_MODE_ISSUER) && (depth == 1)) return true; |
61 | 0 | if ((mode & FR_TLS_VERIFY_MODE_UNTRUSTED) && (depth < untrusted)) return true; |
62 | | |
63 | 0 | return false; |
64 | 0 | } |
65 | | |
66 | | DIAG_OFF(DIAG_UNKNOWN_PRAGMAS) |
67 | | DIAG_OFF(used-but-marked-unused) /* fix spurious warnings for sk macros */ |
68 | | |
69 | | /** Print verbose humanly readable messages about why certificate validation failed |
70 | | * |
71 | | */ |
72 | | static void tls_verify_error_detail(request_t *request, SSL_CTX *ctx, int err) |
73 | 0 | { |
74 | 0 | X509_STORE *store = SSL_CTX_get_ex_data(ctx, FR_TLS_EX_CTX_INDEX_VERIFY_STORE); |
75 | |
|
76 | 0 | switch (err) { |
77 | | /* |
78 | | * We linked the provided cert to at least one |
79 | | * other in a chain, but the chain doesn't terminate |
80 | | * in a root CA. |
81 | | */ |
82 | 0 | case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: |
83 | 0 | FALL_THROUGH; |
84 | | |
85 | | /* |
86 | | * We failed to link the provided cert to any |
87 | | * other local certificates in the chain. |
88 | | */ |
89 | 0 | case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: |
90 | 0 | RDEBUG2("Static certificates in verification store are"); |
91 | 0 | if (RDEBUG_ENABLED2) { |
92 | 0 | RINDENT(); |
93 | 0 | fr_tls_x509_objects_log(request, L_DBG, X509_STORE_get0_objects(store)); |
94 | 0 | REXDENT(); |
95 | 0 | } |
96 | 0 | break; |
97 | | |
98 | 0 | default: |
99 | 0 | break; |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | | /** Validates a certificate using custom logic |
104 | | * |
105 | | * Before trusting a certificate, we make sure that the certificate is |
106 | | * 'valid'. There are several checks we perform to verify its validity. |
107 | | * |
108 | | * 1. Verify the certificate's signature, and verifying that the certificate has |
109 | | * been issued by a trusted Certificate Authority (this is done for us by OpenSSL). |
110 | | * |
111 | | * 2. Verify that the certificate is valid for the present date (i.e. it is being |
112 | | * presented within its validity dates). |
113 | | * |
114 | | * 3. Verify that the certificate has not been revoked by its issuing Certificate |
115 | | * Authority, by checking with respect to a Certificate Revocation List (CRL). |
116 | | * |
117 | | * @note This callback will be called multiple times based on the depth of the root |
118 | | * certificate chain. |
119 | | * |
120 | | * @note As a byproduct of validation, various OIDs will be extracted from the |
121 | | * certificates, and inserted into the session-state list as fr_pair_t. |
122 | | * |
123 | | * @param ok preverify ok. 1 if true, 0 if false. |
124 | | * @param x509_ctx containing certs to verify. |
125 | | * @return |
126 | | * - 0 if not valid. |
127 | | * - 1 if valid. |
128 | | */ |
129 | | int fr_tls_verify_cert_cb(int ok, X509_STORE_CTX *x509_ctx) |
130 | 0 | { |
131 | 0 | X509 *cert; |
132 | |
|
133 | 0 | SSL_CTX *ssl_ctx; |
134 | 0 | SSL *ssl; |
135 | 0 | fr_tls_session_t *tls_session; |
136 | 0 | int err, depth; |
137 | 0 | fr_tls_conf_t *conf; |
138 | 0 | int my_ok = ok; |
139 | 0 | int untrusted; |
140 | |
|
141 | 0 | request_t *request; |
142 | 0 | fr_pair_t *container = NULL; |
143 | 0 | fr_pair_t *depth_pair; |
144 | |
|
145 | 0 | cert = X509_STORE_CTX_get_current_cert(x509_ctx); |
146 | 0 | err = X509_STORE_CTX_get_error(x509_ctx); |
147 | 0 | depth = X509_STORE_CTX_get_error_depth(x509_ctx); |
148 | 0 | untrusted = X509_STORE_CTX_get_num_untrusted(x509_ctx); |
149 | | |
150 | | /* |
151 | | * Retrieve the pointer to the SSL of the connection currently treated |
152 | | * and the application specific data stored into the SSL object. |
153 | | */ |
154 | 0 | ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); |
155 | 0 | ssl_ctx = SSL_get_SSL_CTX(ssl); |
156 | 0 | conf = fr_tls_session_conf(ssl); |
157 | 0 | tls_session = talloc_get_type_abort(SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_TLS_SESSION), fr_tls_session_t); |
158 | 0 | request = fr_tls_session_request(tls_session->ssl); |
159 | | |
160 | | /* |
161 | | * If this error appears it suggests |
162 | | * that OpenSSL is trying to perform post-handshake |
163 | | * certificate validation which we don't support. |
164 | | */ |
165 | 0 | if (!tls_session->can_pause) { |
166 | 0 | fr_assert_msg("Unexpected call to %s. " |
167 | 0 | "tls_session_async_handshake_cont must be in call stack", __FUNCTION__); |
168 | 0 | return 0; |
169 | 0 | } |
170 | | |
171 | | /* |
172 | | * Bail out as quickly as possible, producing |
173 | | * as few errors as possible. |
174 | | */ |
175 | 0 | if (unlang_request_is_cancelled(request)) { |
176 | 0 | X509_STORE_CTX_set_error(x509_ctx, 0); |
177 | 0 | return 1; |
178 | 0 | } |
179 | | |
180 | | /* |
181 | | * Find or add the chain depth attribute and record the greatest depth we see + 1, |
182 | | * as depth is zero based. |
183 | | */ |
184 | 0 | if (unlikely(fr_pair_find_or_append_by_da(request->session_state_ctx, &depth_pair, &request->session_state_pairs, |
185 | 0 | attr_tls_certificate_chain_depth) < 0)) { |
186 | 0 | RERROR("Failed to add certificate chain depth pair"); |
187 | 0 | return 0; |
188 | 0 | } |
189 | 0 | if (depth_pair->vp_uint32 < ((uint32_t)depth) + 1) depth_pair->vp_uint32 = (uint32_t)depth + 1; |
190 | |
|
191 | 0 | if (RDEBUG_ENABLED3) { |
192 | 0 | char subject[2048]; |
193 | 0 | STACK_OF(X509) *our_chain; |
194 | 0 | int i; |
195 | |
|
196 | 0 | our_chain = X509_STORE_CTX_get0_chain(x509_ctx); |
197 | 0 | RDEBUG3("Certificate chain - %i cert(s) untrusted", untrusted); |
198 | 0 | for (i = sk_X509_num(our_chain); i > 0 ; i--) { |
199 | 0 | X509 *this_cert = sk_X509_value(our_chain, i - 1); |
200 | |
|
201 | 0 | X509_NAME_oneline(X509_get_subject_name(this_cert), subject, sizeof(subject)); |
202 | 0 | subject[sizeof(subject) - 1] = '\0'; |
203 | |
|
204 | 0 | RDEBUG3("%s [%i] %s", this_cert == cert ? ">" : " ", i - 1, subject); |
205 | 0 | } |
206 | 0 | } |
207 | | |
208 | | /* |
209 | | * See if the user has disabled verification for |
210 | | * this certificate. If they have, force verification |
211 | | * to succeed. |
212 | | */ |
213 | 0 | if (!my_ok) { |
214 | 0 | char const *p = X509_verify_cert_error_string(err); |
215 | 0 | if (!verify_applies(conf->verify.mode, depth, untrusted)) { |
216 | 0 | RDEBUG2("Ignoring verification error - %s (%i)", p, err); |
217 | 0 | tls_verify_error_detail(request, ssl_ctx, err); |
218 | |
|
219 | 0 | my_ok = 1; |
220 | 0 | X509_STORE_CTX_set_error(x509_ctx, 0); |
221 | 0 | } else { |
222 | 0 | RERROR("Verification error - %s (%i)", p, err); |
223 | 0 | tls_verify_error_detail(request, ssl_ctx, err); |
224 | 0 | goto done; |
225 | 0 | } |
226 | 0 | } |
227 | | |
228 | 0 | if (verify_applies(conf->verify.attribute_mode, depth, untrusted) && |
229 | 0 | (!(container = fr_pair_find_by_da_idx(&request->session_state_pairs, attr_tls_certificate, depth)) || |
230 | 0 | fr_pair_list_empty(&container->vp_group))) { |
231 | 0 | if (!container) { |
232 | 0 | unsigned int i; |
233 | | |
234 | | /* |
235 | | * Build a stack of container attributes. |
236 | | * |
237 | | * OpenSSL passes us the deepest certificate |
238 | | * first, so we need to build out sufficient |
239 | | * TLS-Certificate container TLVs so the TLS-Certificate |
240 | | * indexes match the attribute depth. |
241 | | */ |
242 | 0 | for (i = fr_pair_count_by_da(&request->session_state_pairs, attr_tls_certificate); |
243 | 0 | i <= (unsigned int)depth; |
244 | 0 | i++) { |
245 | 0 | MEM(container = fr_pair_afrom_da(request->session_state_ctx, attr_tls_certificate)); |
246 | 0 | fr_pair_append(&request->session_state_pairs, container); |
247 | 0 | } |
248 | 0 | } |
249 | |
|
250 | | #ifdef STATIC_ANALYZER |
251 | | /* |
252 | | * Container can never be NULL, because if container |
253 | | * was previously NULL, i will be <= depth. |
254 | | */ |
255 | | if (!fr_cond_assert(container)) { |
256 | | my_ok = 0; |
257 | | goto done; |
258 | | } |
259 | | #endif |
260 | | /* |
261 | | * If we fail to populate the cert attributes, |
262 | | * trash all instances in the session-state list |
263 | | * and cause validation to fail. |
264 | | */ |
265 | 0 | if (fr_tls_session_pairs_from_x509_cert(&container->vp_group, container, |
266 | 0 | request, cert, conf->verify.der_decode) < 0) { |
267 | 0 | fr_pair_delete_by_da(&request->session_state_pairs, attr_tls_certificate); |
268 | 0 | if (conf->verify.der_decode) { |
269 | 0 | fr_pair_delete_by_da(&request->session_state_pairs, attr_der_certificate); |
270 | 0 | } |
271 | 0 | my_ok = 0; |
272 | 0 | goto done; |
273 | 0 | } |
274 | | |
275 | 0 | log_request_pair(L_DBG_LVL_2, request, NULL, container, "session-state."); |
276 | 0 | } |
277 | 0 | done: |
278 | | /* |
279 | | * If verification hasn't already failed |
280 | | * and we're meant to verify this cert |
281 | | * then call the virtual server. |
282 | | * |
283 | | * We only call the virtual server for |
284 | | * the certificate at depth 0 as all |
285 | | * other certificate attributes should |
286 | | * have been added by this point. |
287 | | */ |
288 | 0 | if (my_ok && (depth == 0)) { |
289 | 0 | if (conf->verify_certificate && tls_session->verify_client_cert) { |
290 | 0 | RDEBUG2("Requesting certificate validation"); |
291 | | |
292 | | /* |
293 | | * This sets the validation state of the tls_session |
294 | | * so that when we call ASYNC_pause_job(), and execution |
295 | | * jumps back to tls_session_async_handshake_cont |
296 | | * (just under SSL_read()) |
297 | | * the code there knows what job it needs to push onto |
298 | | * the unlang stack. |
299 | | */ |
300 | 0 | fr_tls_verify_cert_request(tls_session, SSL_session_reused(tls_session->ssl)); |
301 | | |
302 | | /* |
303 | | * Jumps back to SSL_read() in session.c |
304 | | * |
305 | | * Be aware that if the request is cancelled |
306 | | * whatever was meant to be done during the |
307 | | * time we yielded may not have been completed. |
308 | | */ |
309 | 0 | ASYNC_pause_job(); |
310 | | |
311 | | /* |
312 | | * Just try and bail out as quickly as possible. |
313 | | */ |
314 | 0 | if (unlang_request_is_cancelled(request)) { |
315 | 0 | X509_STORE_CTX_set_error(x509_ctx, 0); |
316 | 0 | fr_tls_verify_cert_reset(tls_session); |
317 | 0 | return 1; |
318 | 0 | } |
319 | | |
320 | | |
321 | | /* |
322 | | * If we couldn't validate the certificate |
323 | | * then validation overall fails. |
324 | | */ |
325 | 0 | if (!fr_tls_verify_cert_result(tls_session)) { |
326 | 0 | REDEBUG("Certificate validation failed"); |
327 | 0 | my_ok = 0; |
328 | 0 | X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION); |
329 | 0 | } |
330 | 0 | } |
331 | 0 | } |
332 | | |
333 | 0 | tls_session->client_cert_ok = (my_ok > 0); |
334 | 0 | RDEBUG2("[verify] = %s", my_ok ? "ok" : "invalid"); |
335 | |
|
336 | 0 | return my_ok; |
337 | 0 | } |
338 | | DIAG_ON(used-but-marked-unused) |
339 | | DIAG_ON(DIAG_UNKNOWN_PRAGMAS) |
340 | | |
341 | | /** Revalidates the client's certificate chain |
342 | | * |
343 | | * Wraps the fr_tls_verify_cert_cb callback, allowing us to use the same |
344 | | * validation logic whenever we need to. |
345 | | * |
346 | | * @note Only use so far is forcing the chain to be re-validated on session |
347 | | * resumption. |
348 | | * |
349 | | * @return |
350 | | * - 1 if the chain could be validated. |
351 | | * - 0 if the chain failed validation. |
352 | | */ |
353 | | int fr_tls_verify_cert_chain(request_t *request, SSL *ssl) |
354 | 0 | { |
355 | 0 | int err; |
356 | 0 | int verify; |
357 | 0 | int ret = 1; |
358 | |
|
359 | 0 | SSL_CTX *ssl_ctx; |
360 | 0 | STACK_OF(X509) *chain; |
361 | 0 | X509 *cert; |
362 | 0 | X509_STORE *store; |
363 | 0 | X509_STORE_CTX *store_ctx; |
364 | | |
365 | | /* |
366 | | * If there's no client certificate, we just return OK. |
367 | | */ |
368 | 0 | cert = SSL_get0_peer_certificate(ssl); /* Does not increase ref count */ |
369 | 0 | if (!cert) return 1; |
370 | | |
371 | 0 | ssl_ctx = SSL_get_SSL_CTX(ssl); |
372 | 0 | store_ctx = X509_STORE_CTX_new(); |
373 | 0 | if (unlikely(store_ctx == NULL)) { |
374 | 0 | REDEBUG("Failed allocating X509_STORE_CTX"); |
375 | 0 | return 0; |
376 | 0 | } |
377 | | |
378 | 0 | chain = SSL_get_peer_cert_chain(ssl); /* Does not increase ref count */ |
379 | 0 | store = SSL_CTX_get_ex_data(ssl_ctx, FR_TLS_EX_CTX_INDEX_VERIFY_STORE); /* Gets the verification store */ |
380 | | |
381 | | /* |
382 | | * This sets up a store_ctx for doing peer certificate verification. |
383 | | * |
384 | | * store_ctx - Is the ctx to initialise |
385 | | * store - Is an X509_STORE of implicitly |
386 | | * trusted certificates. Here we're using |
387 | | * the verify store that was created when we |
388 | | * allocated the SSL_CTX. |
389 | | * cert - Is the certificate to validate. |
390 | | * chain - Is any other certificates the peer provided |
391 | | * us in order to build a chain from a trusted |
392 | | * root or intermediary to its leaf (cert). |
393 | | * |
394 | | * Note: SSL_CTX_get_cert_store() returns the ctx->cert_store, which |
395 | | * is not the same as the verification cert store. |
396 | | */ |
397 | 0 | if (unlikely(X509_STORE_CTX_init(store_ctx, store, cert, chain) != 1)) { |
398 | 0 | REDEBUG("Failed initialising X509_STORE_CTX"); |
399 | 0 | X509_STORE_CTX_free(store_ctx); |
400 | 0 | return 0; |
401 | 0 | } |
402 | | |
403 | 0 | X509_STORE_CTX_set_ex_data(store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx(), ssl); |
404 | 0 | X509_STORE_CTX_set_verify_cb(store_ctx, fr_tls_verify_cert_cb); |
405 | |
|
406 | 0 | verify = X509_verify_cert(store_ctx); |
407 | 0 | if (verify != 1) { |
408 | 0 | err = X509_STORE_CTX_get_error(store_ctx); |
409 | |
|
410 | 0 | if (err != X509_V_OK) { |
411 | 0 | REDEBUG("Failed re-validating resumed session: %s", X509_verify_cert_error_string(err)); |
412 | 0 | ret = 0; |
413 | 0 | } |
414 | 0 | } |
415 | |
|
416 | 0 | X509_STORE_CTX_free(store_ctx); |
417 | |
|
418 | 0 | return ret; |
419 | 0 | } |
420 | | |
421 | | /** Process the result of `verify certificate { ... }` |
422 | | * |
423 | | */ |
424 | | static unlang_action_t tls_verify_client_cert_result(request_t *request, void *uctx) |
425 | 0 | { |
426 | 0 | fr_tls_session_t *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t); |
427 | 0 | fr_pair_t *vp, *next; |
428 | |
|
429 | 0 | fr_assert(tls_session->validate.state == FR_TLS_VALIDATION_REQUESTED); |
430 | |
|
431 | 0 | vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_tls_packet_type); |
432 | 0 | if (!vp || (vp->vp_uint32 != enum_tls_packet_type_success->vb_uint32)) { |
433 | 0 | REDEBUG("Failed (re-)validating certificates"); |
434 | | |
435 | | /* |
436 | | * Hoist any instances of Module-Failure-Message from the subrequest |
437 | | * so they can be used for logging failures. |
438 | | */ |
439 | 0 | vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_module_failure_message); |
440 | 0 | while (vp && request->parent) { |
441 | 0 | next = fr_pair_find_by_da(&request->request_pairs, vp, attr_module_failure_message); |
442 | 0 | fr_pair_remove(&request->request_pairs, vp); |
443 | 0 | fr_pair_steal_append(request->parent->request_ctx, &request->parent->request_pairs, vp); |
444 | 0 | vp = next; |
445 | 0 | } |
446 | |
|
447 | 0 | tls_session->validate.state = FR_TLS_VALIDATION_FAILED; |
448 | 0 | return UNLANG_ACTION_CALCULATE_RESULT; |
449 | 0 | } |
450 | | |
451 | 0 | tls_session->validate.state = FR_TLS_VALIDATION_SUCCESS; |
452 | |
|
453 | 0 | RDEBUG2("Certificates (re-)validated"); |
454 | |
|
455 | 0 | return UNLANG_ACTION_CALCULATE_RESULT; |
456 | 0 | } |
457 | | |
458 | | /** Push a `verify certificate { ... }` call into the current request, using a subrequest |
459 | | * |
460 | | * @param[in] request The current request. |
461 | | * @param[in] tls_session The current TLS session. |
462 | | * @return |
463 | | * - UNLANG_ACTION_CALCULATE_RESULT on noop. |
464 | | * - UNLANG_ACTION_PUSHED_CHILD on success. |
465 | | * - UNLANG_ACTION_FAIL on failure. |
466 | | */ |
467 | | static unlang_action_t tls_verify_client_cert_push(request_t *request, fr_tls_session_t *tls_session) |
468 | 0 | { |
469 | 0 | fr_tls_conf_t *conf = fr_tls_session_conf(tls_session->ssl); |
470 | 0 | request_t *child; |
471 | 0 | fr_pair_t *vp; |
472 | 0 | unlang_action_t ua; |
473 | |
|
474 | 0 | MEM(child = unlang_subrequest_alloc(request, dict_tls)); |
475 | 0 | request = child; |
476 | | |
477 | | /* |
478 | | * Add extra pairs to the subrequest |
479 | | */ |
480 | 0 | fr_tls_session_extra_pairs_copy_to_child(child, tls_session); |
481 | | |
482 | | /* |
483 | | * Setup the child request for loading |
484 | | * session resumption data. |
485 | | */ |
486 | 0 | MEM(pair_prepend_request(&vp, attr_tls_packet_type) >= 0); |
487 | 0 | vp->vp_uint32 = enum_tls_packet_type_verify_certificate->vb_uint32; |
488 | | |
489 | | /* |
490 | | * Copy certificate pairs to the child session state |
491 | | */ |
492 | 0 | vp = NULL; |
493 | 0 | while ((vp = fr_pair_find_by_da(&request->parent->session_state_pairs, vp, attr_tls_certificate))) { |
494 | 0 | fr_pair_append(&request->session_state_pairs, fr_pair_copy(request->session_state_ctx, vp)); |
495 | 0 | } |
496 | 0 | if (conf->verify.der_decode) { |
497 | 0 | while ((vp = fr_pair_find_by_da(&request->parent->session_state_pairs, vp, attr_der_certificate))) { |
498 | 0 | fr_pair_append(&request->session_state_pairs, fr_pair_copy(request->session_state_ctx, vp)); |
499 | 0 | } |
500 | 0 | } |
501 | |
|
502 | 0 | MEM(pair_append_request(&vp, attr_tls_session_resumed) >= 0); |
503 | 0 | vp->vp_bool = tls_session->validate.resumed; |
504 | | |
505 | | /* |
506 | | * Allocate a child, and set it up to call |
507 | | * the TLS virtual server. |
508 | | */ |
509 | 0 | ua = fr_tls_call_push(child, tls_verify_client_cert_result, conf, tls_session, false); |
510 | 0 | if (ua < 0) { |
511 | 0 | PERROR("Failed calling TLS virtual server"); |
512 | 0 | talloc_free(child); |
513 | 0 | return UNLANG_ACTION_FAIL; |
514 | 0 | } |
515 | | |
516 | 0 | return ua; |
517 | 0 | } |
518 | | |
519 | | /** Clear any previous validation result |
520 | | * |
521 | | * Should be called by the validation requestor to get the result and reset |
522 | | * the validation state. |
523 | | * |
524 | | * @return |
525 | | * - true if the certificate chain was validated. |
526 | | * - false if the certificate chain failed validation. |
527 | | */ |
528 | | bool fr_tls_verify_cert_result(fr_tls_session_t *tls_session) |
529 | 0 | { |
530 | 0 | bool result; |
531 | |
|
532 | 0 | fr_assert(tls_session->validate.state != FR_TLS_VALIDATION_INIT); |
533 | |
|
534 | 0 | result = tls_session->validate.state == FR_TLS_VALIDATION_SUCCESS; |
535 | |
|
536 | 0 | tls_session->validate.state = FR_TLS_VALIDATION_INIT; |
537 | 0 | tls_session->validate.resumed = false; |
538 | |
|
539 | 0 | return result; |
540 | 0 | } |
541 | | |
542 | | /** Reset the verification state |
543 | | * |
544 | | */ |
545 | | void fr_tls_verify_cert_reset(fr_tls_session_t *tls_session) |
546 | 0 | { |
547 | 0 | tls_session->validate.state = FR_TLS_VALIDATION_INIT; |
548 | 0 | tls_session->validate.resumed = false; |
549 | 0 | } |
550 | | |
551 | | /** Setup a verification request |
552 | | * |
553 | | */ |
554 | | void fr_tls_verify_cert_request(fr_tls_session_t *tls_session, bool session_resumed) |
555 | 0 | { |
556 | 0 | fr_assert(tls_session->validate.state == FR_TLS_VALIDATION_INIT); |
557 | |
|
558 | 0 | tls_session->validate.state = FR_TLS_VALIDATION_REQUESTED; |
559 | 0 | tls_session->validate.resumed = session_resumed; |
560 | 0 | } |
561 | | |
562 | | /** Push a `verify certificate { ... }` section |
563 | | * |
564 | | * @param[in] request The current request. |
565 | | * @param[in] tls_session The current TLS session. |
566 | | * @return |
567 | | * - UNLANG_ACTION_CALCULATE_RESULT - No pending actions |
568 | | * - UNLANG_ACTION_PUSHED_CHILD - Pending operations to evaluate. |
569 | | */ |
570 | | unlang_action_t fr_tls_verify_cert_pending_push(request_t *request, fr_tls_session_t *tls_session) |
571 | 0 | { |
572 | 0 | if (tls_session->validate.state == FR_TLS_VALIDATION_REQUESTED) { |
573 | 0 | return tls_verify_client_cert_push(request, tls_session); |
574 | 0 | } |
575 | | |
576 | 0 | return UNLANG_ACTION_CALCULATE_RESULT; |
577 | 0 | } |
578 | | #endif /* WITH_TLS */ |