Coverage Report

Created: 2026-10-18 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: fdbb16fa726f5935721182da1236b9ad411d1144 $
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 2003 Alan DeKok (aland@freeradius.org)
25
 * @copyright 2006-2016 The FreeRADIUS server project
26
 */
27
#ifdef WITH_TLS
28
#define LOG_PREFIX "tls"
29
30
#define _TLS_PRIVATE 1
31
32
#include <freeradius-devel/server/exec.h>
33
#include <freeradius-devel/server/pair.h>
34
#include <freeradius-devel/tls/log.h>
35
#include <freeradius-devel/unlang/function.h>
36
#include <freeradius-devel/unlang/subrequest.h>
37
#include <freeradius-devel/util/debug.h>
38
#include <freeradius-devel/util/strerror.h>
39
#include <freeradius-devel/util/syserror.h>
40
41
#include <freeradius-devel/protocol/tls/freeradius.h>
42
43
#include <openssl/x509v3.h>
44
45
#include "attrs.h"
46
#include "base.h"
47
48
/** Check to see if a verification operation should apply to a certificate
49
 *
50
 * @param[in] depth starting at 0.
51
 *      Certificate 0 is the leaf cert (i.e. the client or server cert);
52
 * @param[in] untrusted The number of untrusted certificates.
53
 * @param[in] mode  to check
54
 * @return
55
 *  - true if a given validation check should apply.
56
 **     - false if a validation check should not apply.
57
 */
58
static inline CC_HINT(always_inline)
59
bool verify_applies(fr_tls_verify_mode_t mode, int depth, int untrusted)
60
0
{
61
0
  if (mode == FR_TLS_VERIFY_MODE_ALL) return true;
62
0
  if (mode == FR_TLS_VERIFY_MODE_DISABLED) return false;
63
64
0
  if ((mode & FR_TLS_VERIFY_MODE_LEAF) && (depth == 0)) return true;
65
0
  if ((mode & FR_TLS_VERIFY_MODE_ISSUER) && (depth == 1)) return true;
66
0
  if ((mode & FR_TLS_VERIFY_MODE_UNTRUSTED) && (depth < untrusted)) return true;
67
68
0
  return false;
69
0
}
70
71
DIAG_OFF_OPTIONAL(used-but-marked-unused) /* fix spurious warnings for sk macros */
72
73
/** Print verbose humanly readable messages about why certificate validation failed
74
 *
75
 */
76
static void tls_verify_error_detail(request_t *request, SSL_CTX *ctx, int err)
77
0
{
78
0
  X509_STORE  *store = SSL_CTX_get_ex_data(ctx, FR_TLS_EX_CTX_INDEX_VERIFY_STORE);
79
80
0
  switch (err) {
81
  /*
82
   *  We linked the provided cert to at least one
83
   *  other in a chain, but the chain doesn't terminate
84
   *  in a root CA.
85
   */
86
0
  case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
87
0
    FALL_THROUGH;
88
89
  /*
90
   *  We failed to link the provided cert to any
91
   *  other local certificates in the chain.
92
   */
93
0
  case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
94
0
    RDEBUG2("(TLS) Static certificates in verification store are");
95
0
    if (RDEBUG_ENABLED2) {
96
0
      RINDENT();
97
0
      fr_tls_x509_objects_log(request, L_DBG, X509_STORE_get0_objects(store));
98
0
      REXDENT();
99
0
    }
100
0
    break;
101
102
0
  default:
103
0
    break;
104
0
  }
105
0
}
106
107
/** Validates a certificate using custom logic
108
 *
109
 * Before trusting a certificate, we make sure that the certificate is
110
 * 'valid'. There are several checks we perform to verify its validity.
111
 *
112
 *   1. Verify the certificate's signature, and verifying that the certificate has
113
 *      been issued by a trusted Certificate Authority (this is done for us by OpenSSL).
114
 *
115
 *   2. Verify that the certificate is valid for the present date (i.e. it is being
116
 *      presented within its validity dates).
117
 *
118
 *   3. Verify that the certificate has not been revoked by its issuing Certificate
119
 *      Authority, by checking with respect to a Certificate Revocation List (CRL).
120
 *
121
 * @note This callback will be called multiple times based on the depth of the root
122
 *  certificate chain.
123
 *
124
 * @note OpenSSL runs the handshake (including this callback) on a fibre with a
125
 *  limited stack (32 KB on OpenSSL releases without ASYNC_set_mem_functions()).
126
 *  Because of that limited stack, we need to perform all certificate decoding
127
 *  outside of the fibre on the main stack, i.e. after OpenSSL has yielded and
128
 *  the handshake has paused.  See fr_tls_verify_cert_pending_push().
129
 *
130
 * @param ok    preverify ok.  1 if true, 0 if false.
131
 * @param x509_ctx  containing certs to verify.
132
 * @return
133
 *  - 0 if not valid.
134
 *  - 1 if valid.
135
 */
136
int fr_tls_verify_cert_cb(int ok, X509_STORE_CTX *x509_ctx)
137
0
{
138
0
  X509      *cert;
139
140
0
  SSL_CTX     *ssl_ctx;
141
0
  SSL     *ssl;
142
0
  fr_tls_session_t  *tls_session;
143
0
  int     err, depth;
144
0
  fr_tls_conf_t   *conf;
145
0
  int     my_ok = ok;
146
0
  int     untrusted;
147
148
0
  request_t   *request;
149
0
  fr_pair_t   *depth_pair;
150
151
0
  cert = X509_STORE_CTX_get_current_cert(x509_ctx);
152
0
  err = X509_STORE_CTX_get_error(x509_ctx);
153
0
  depth = X509_STORE_CTX_get_error_depth(x509_ctx);
154
0
  untrusted = X509_STORE_CTX_get_num_untrusted(x509_ctx);
155
156
  /*
157
   *  Retrieve the pointer to the SSL of the connection currently treated
158
   *  and the application specific data stored into the SSL object.
159
   */
160
0
  ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
161
0
  ssl_ctx = SSL_get_SSL_CTX(ssl);
162
0
  conf = fr_tls_session_conf(ssl);
163
0
  tls_session = talloc_get_type_abort(SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_TLS_SESSION), fr_tls_session_t);
164
0
  request = fr_tls_session_request(tls_session->ssl);
165
166
  /*
167
   *  If this error appears it suggests
168
   *  that OpenSSL is trying to perform post-handshake
169
   *  certificate validation which we don't support.
170
   */
171
0
  if (!tls_session->can_pause) {
172
0
    fr_assert_msg("Unexpected call to %s. "
173
0
            "tls_session_async_handshake_cont must be in call stack", __FUNCTION__);
174
0
    return 0;
175
0
  }
176
177
  /*
178
   *  Bail out as quickly as possible, producing
179
   *  as few errors as possible.
180
   */
181
0
  if (unlang_request_is_cancelled(request)) {
182
0
    X509_STORE_CTX_set_error(x509_ctx, 0);
183
0
    return 1;
184
0
  }
185
186
  /*
187
   *  Find or add the chain depth attribute and record the greatest depth we see + 1,
188
   *  as depth is zero based.
189
   */
190
0
  if (unlikely(fr_pair_find_or_append_by_da(request->session_state_ctx, &depth_pair, &request->session_state_pairs,
191
0
           attr_tls_certificate_chain_depth) < 0)) {
192
0
    REDEBUG("(TLS) Failed to add certificate chain depth pair");
193
0
    return 0;
194
0
  }
195
0
  if (depth_pair->vp_uint32 < ((uint32_t)depth) + 1) depth_pair->vp_uint32 = (uint32_t)depth + 1;
196
197
0
  if (RDEBUG_ENABLED3) {
198
0
    char    subject[2048];
199
0
    STACK_OF(X509)  *our_chain;
200
0
    int   i;
201
202
0
    our_chain = X509_STORE_CTX_get0_chain(x509_ctx);
203
0
    RDEBUG3("(TLS) Certificate chain - %i cert(s) untrusted", untrusted);
204
0
    for (i = sk_X509_num(our_chain); i > 0 ; i--) {
205
0
      X509 *this_cert = sk_X509_value(our_chain, i - 1);
206
207
0
      X509_NAME_oneline(X509_get_subject_name(this_cert), subject, sizeof(subject));
208
0
      subject[sizeof(subject) - 1] = '\0';
209
210
0
      RDEBUG3("(TLS) %s [%i] %s", this_cert == cert ? ">" : " ", i - 1, subject);
211
0
    }
212
0
  }
213
214
  /*
215
   *  See if the user has disabled verification for
216
   *      this certificate.  If they have, force verification
217
   *  to succeed.
218
   */
219
0
  if (!my_ok) {
220
0
    char const *p = X509_verify_cert_error_string(err);
221
0
    if (!verify_applies(conf->verify.mode, depth, untrusted)) {
222
0
      RDEBUG2("(TLS) Ignoring verification error - %s (%i)", p, err);
223
0
      tls_verify_error_detail(request, ssl_ctx, err);
224
225
0
      my_ok = 1;
226
0
      X509_STORE_CTX_set_error(x509_ctx, 0);
227
0
    } else {
228
0
      REDEBUG("(TLS) Verification error - %s (%i)", p, err);
229
0
      fr_tls_session_error_add(request, FR_ERROR_VALUE_CERTIFICATE_VERIFY_FAILED);
230
0
      tls_verify_error_detail(request, ssl_ctx, err);
231
0
      goto done;
232
0
    }
233
0
  }
234
235
0
done:
236
  /*
237
   *  The chain is complete once OpenSSL reaches depth 0.  Pause the handshake
238
   *  here so that fr_tls_verify_cert_pending_push() can decode the chain and run
239
   *  the `verify certificate` section on the main stack.  Execution continues
240
   *  after the ASYNC_pause_job() call below once the section has run.
241
   */
242
0
  if (my_ok && (depth == 0)) {
243
0
    bool want_pairs = conf->verify.attribute_mode != FR_TLS_VERIFY_MODE_DISABLED;
244
0
    bool want_verify = conf->verify_certificate && tls_session->verify_peer_cert;
245
246
0
    if (want_pairs || want_verify) {
247
0
      fr_tls_verify_cert_request(tls_session, x509_ctx);
248
249
      /*
250
       *  Be aware that if the request is cancelled
251
       *  whatever was meant to be done during the
252
       *  time we yielded may not have been completed.
253
       */
254
0
      ASYNC_pause_job();
255
256
      /*
257
       *  Just try and bail out as quickly as possible.
258
       */
259
0
      if (unlang_request_is_cancelled(request)) {
260
0
        X509_STORE_CTX_set_error(x509_ctx, 0);
261
0
        fr_tls_verify_cert_reset(tls_session);
262
0
        return 1;
263
0
      }
264
265
      /*
266
       *  If we couldn't decode the chain, or the
267
       *  `verify certificate` section rejected it,
268
       *  then validation overall fails.
269
       */
270
0
      if (!fr_tls_verify_cert_result(tls_session)) {
271
0
        REDEBUG("(TLS) Certificate validation failed");
272
0
        my_ok = 0;
273
0
        X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
274
0
      }
275
0
    }
276
0
  }
277
278
0
  tls_session->peer_cert_ok = (my_ok > 0);
279
0
  RDEBUG2("(TLS) [verify] = %s", my_ok ? "ok" : "invalid");
280
281
0
  return my_ok;
282
0
}
283
DIAG_ON_OPTIONAL(used-but-marked-unused)
284
285
/** Revalidates the client's certificate chain
286
 *
287
 * Wraps the fr_tls_verify_cert_cb callback, allowing us to use the same
288
 * validation logic whenever we need to.
289
 *
290
 * @note Only use so far is forcing the chain to be re-validated on session
291
 *  resumption.
292
 *
293
 * @return
294
 *  - 1 if the chain could be validated.
295
 *  - 0 if the chain failed validation.
296
 */
297
int fr_tls_verify_cert_chain(request_t *request, SSL *ssl)
298
0
{
299
0
  int   err;
300
0
  int   verify;
301
0
  int   ret = 1;
302
303
0
  SSL_CTX   *ssl_ctx;
304
0
  STACK_OF(X509)  *chain;
305
0
  X509    *cert;
306
0
  X509_STORE  *store;
307
0
  X509_STORE_CTX  *store_ctx;
308
309
  /*
310
   *  If there's no client certificate, we just return OK.
311
   */
312
0
  cert = SSL_get0_peer_certificate(ssl);      /* Does not increase ref count */
313
0
  if (!cert) return 1;
314
315
0
  ssl_ctx = SSL_get_SSL_CTX(ssl);
316
0
  store_ctx = X509_STORE_CTX_new();
317
0
  if (unlikely(store_ctx == NULL)) {
318
0
    REDEBUG("(TLS) Failed allocating X509_STORE_CTX");
319
0
    return 0;
320
0
  }
321
322
0
  chain = SSL_get_peer_cert_chain(ssl);     /* Does not increase ref count */
323
0
  store = SSL_CTX_get_ex_data(ssl_ctx, FR_TLS_EX_CTX_INDEX_VERIFY_STORE);  /* Gets the verification store */
324
325
  /*
326
   *  This sets up a store_ctx for doing peer certificate verification.
327
   *
328
   *  store_ctx - Is the ctx to initialise
329
   *  store   - Is an X509_STORE of implicitly
330
   *        trusted certificates.  Here we're using
331
   *        the verify store that was created when we
332
   *        allocated the SSL_CTX.
333
   *  cert    - Is the certificate to validate.
334
   *  chain   - Is any other certificates the peer provided
335
   *        us in order to build a chain from a trusted
336
   *            root or intermediary to its leaf (cert).
337
   *
338
   *  Note: SSL_CTX_get_cert_store() returns the ctx->cert_store, which
339
   *      is not the same as the verification cert store.
340
   */
341
0
  if (unlikely(X509_STORE_CTX_init(store_ctx, store, cert, chain) != 1)) {
342
0
    REDEBUG("(TLS) Failed initialising X509_STORE_CTX");
343
0
    X509_STORE_CTX_free(store_ctx);
344
0
    return 0;
345
0
  }
346
347
0
  X509_STORE_CTX_set_ex_data(store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx(), ssl);
348
0
  X509_STORE_CTX_set_verify_cb(store_ctx, fr_tls_verify_cert_cb);
349
350
0
  verify = X509_verify_cert(store_ctx);
351
0
  if (verify != 1) {
352
0
    err = X509_STORE_CTX_get_error(store_ctx);
353
354
0
    if (err != X509_V_OK) {
355
0
      REDEBUG("(TLS) Failed re-validating resumed session: %s", X509_verify_cert_error_string(err));
356
0
      fr_tls_session_error_add(request, FR_ERROR_VALUE_CERTIFICATE_REVALIDATION_FAILED);
357
0
      ret = 0;
358
0
    }
359
0
  }
360
361
0
  X509_STORE_CTX_free(store_ctx);
362
363
0
  return ret;
364
0
}
365
366
/** Process the result of `verify certificate { ... }`
367
 *
368
 */
369
static unlang_action_t tls_verify_peer_cert_result(request_t *request, void *uctx)
370
0
{
371
0
  fr_tls_session_t  *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t);
372
0
  fr_pair_t   *vp, *next;
373
374
0
  fr_assert(tls_session->validate.state == FR_TLS_VALIDATION_REQUESTED);
375
376
0
  vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_tls_packet_type);
377
0
  if (!vp || (vp->vp_uint32 != enum_tls_packet_type_success->vb_uint32)) {
378
0
    REDEBUG("(TLS) Failed (re-)validating certificates");
379
0
    fr_tls_session_error_add(request->parent, FR_ERROR_VALUE_VERIFY_CERTIFICATE_FAILED);
380
381
    /*
382
     *  Hoist any instances of Module-Failure-Message from the subrequest
383
     *  so they can be used for logging failures.
384
     */
385
0
    vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_module_failure_message);
386
0
    while (vp && request->parent) {
387
0
      next = fr_pair_find_by_da(&request->request_pairs, vp, attr_module_failure_message);
388
0
      fr_pair_remove(&request->request_pairs, vp);
389
0
      fr_pair_steal_append(request->parent->request_ctx, &request->parent->request_pairs, vp);
390
0
      vp = next;
391
0
    }
392
393
0
    tls_session->validate.state = FR_TLS_VALIDATION_FAILED;
394
0
    return UNLANG_ACTION_CALCULATE_RESULT;
395
0
  }
396
397
0
  tls_session->validate.state = FR_TLS_VALIDATION_SUCCESS;
398
399
0
  RDEBUG2("(TLS) Certificates (re-)validated");
400
401
0
  return UNLANG_ACTION_CALCULATE_RESULT;
402
0
}
403
404
/** Push a `verify certificate { ... }` call into the current request, using a subrequest
405
 *
406
 * @param[in] request   The current request.
407
 * @param[in] tls_session The current TLS session.
408
 * @return
409
 *      - UNLANG_ACTION_CALCULATE_RESULT on noop.
410
 *  - UNLANG_ACTION_PUSHED_CHILD on success.
411
 *      - UNLANG_ACTION_FAIL on failure.
412
 */
413
static unlang_action_t tls_verify_peer_cert_push(request_t *request, fr_tls_session_t *tls_session)
414
0
{
415
0
  fr_tls_conf_t   *conf = tls_session->conf;
416
0
  request_t   *child;
417
0
  fr_pair_t   *vp;
418
0
  unlang_action_t   ua;
419
420
0
  fr_assert(conf->virtual_server);
421
422
0
  MEM(child = tls_subrequest_alloc(request, enum_tls_packet_type_verify_certificate->vb_uint32,
423
0
           &tls_session->session_id));
424
0
  request = child;
425
426
  /*
427
   *  Add extra pairs to the subrequest
428
   */
429
0
  fr_tls_session_extra_pairs_copy_to_child(child, tls_session);
430
431
  /*
432
   *  Copy certificate pairs to the child session state
433
   */
434
0
  vp = NULL;
435
0
  while ((vp = fr_pair_find_by_da(&request->parent->session_state_pairs, vp, attr_tls_certificate))) {
436
0
    fr_pair_append(&request->session_state_pairs, fr_pair_copy(request->session_state_ctx, vp));
437
0
  }
438
0
  if (conf->verify.der_decode) {
439
0
    while ((vp = fr_pair_find_by_da(&request->parent->session_state_pairs, vp, attr_der_certificate))) {
440
0
      fr_pair_append(&request->session_state_pairs, fr_pair_copy(request->session_state_ctx, vp));
441
0
    }
442
0
  }
443
444
0
  MEM(pair_append_request(&vp, attr_tls_session_resumed) >= 0);
445
0
  vp->vp_bool = tls_session->validate.resumed;
446
447
  /*
448
   *  Allocate a child, and set it up to call
449
   *      the TLS virtual server.
450
   */
451
0
  ua = fr_tls_call_push(child, tls_verify_peer_cert_result, conf, tls_session, false);
452
0
  if (ua == UNLANG_ACTION_FAIL) {
453
0
          REDEBUG("(TLS) Failed calling TLS virtual server");
454
0
    talloc_free(child);
455
0
    return UNLANG_ACTION_FAIL;
456
0
  }
457
458
0
  return ua;
459
0
}
460
461
/** Clear any previous validation result
462
 *
463
 * Should be called by the validation requestor to get the result and reset
464
 * the validation state.
465
 *
466
 * @return
467
 *  - true if the certificate chain was validated.
468
 *  - false if the certificate chain failed validation.
469
 */
470
bool fr_tls_verify_cert_result(fr_tls_session_t *tls_session)
471
0
{
472
0
  bool result;
473
474
0
  fr_assert(tls_session->validate.state != FR_TLS_VALIDATION_INIT);
475
476
0
  result = tls_session->validate.state == FR_TLS_VALIDATION_SUCCESS;
477
478
0
  tls_session->validate.state = FR_TLS_VALIDATION_INIT;
479
0
  tls_session->validate.resumed = false;
480
481
0
  return result;
482
0
}
483
484
/** Reset the verification state
485
 *
486
 */
487
void fr_tls_verify_cert_reset(fr_tls_session_t *tls_session)
488
0
{
489
0
  tls_session->validate.state = FR_TLS_VALIDATION_INIT;
490
0
  tls_session->validate.resumed = false;
491
0
  tls_session->validate.x509_ctx = NULL;
492
0
}
493
494
/** Request validation of the chain OpenSSL is verifying
495
 *
496
 * Called by fr_tls_verify_cert_cb() before it pauses the handshake.
497
 *
498
 * @param[in] tls_session The current TLS session.
499
 * @param[in] x509_ctx    holding the chain.  Only valid while the
500
 *        handshake is paused inside the callback.
501
 */
502
void fr_tls_verify_cert_request(fr_tls_session_t *tls_session, X509_STORE_CTX *x509_ctx)
503
0
{
504
0
  fr_assert(tls_session->validate.state == FR_TLS_VALIDATION_INIT);
505
506
0
  tls_session->validate.state = FR_TLS_VALIDATION_REQUESTED;
507
0
  TLS_PENDING_SET(tls_session, FR_TLS_PENDING_VERIFY);
508
0
  tls_session->validate.resumed = false;
509
0
  tls_session->validate.x509_ctx = x509_ctx;
510
0
}
511
512
/** Request re-validation of the certificate pairs restored with a resumed session
513
 *
514
 * Called by the session cache code before it pauses the handshake.  A resumed
515
 * session has no chain to decode, the `TLS-Certificate` pairs came back with
516
 * the session.
517
 *
518
 * @param[in] tls_session The current TLS session.
519
 */
520
void fr_tls_verify_resumed_request(fr_tls_session_t *tls_session)
521
0
{
522
0
  fr_assert(tls_session->validate.state == FR_TLS_VALIDATION_INIT);
523
524
0
  tls_session->validate.state = FR_TLS_VALIDATION_REQUESTED;
525
0
  TLS_PENDING_SET(tls_session, FR_TLS_PENDING_VERIFY);
526
0
  tls_session->validate.resumed = true;
527
0
  tls_session->validate.x509_ctx = NULL;
528
0
}
529
530
DIAG_OFF_OPTIONAL(used-but-marked-unused) /* fix spurious warnings for sk macros */
531
/** Decode the chain OpenSSL is verifying into `session-state.TLS-Certificate` pairs
532
 *
533
 * Runs on the request's own stack while the handshake is paused in
534
 * fr_tls_verify_cert_cb().  The certificate at depth 0 is the peer's, and
535
 * `TLS-Certificate[n]` holds the certificate at depth n.
536
 *
537
 * @param[in] request   The current request.
538
 * @param[in] tls_session The current TLS session.
539
 * @param[in] conf    The TLS configuration.
540
 * @return
541
 *  - 0 on success.
542
 *  - -1 if a certificate could not be decoded.  No `TLS-Certificate`
543
 *    pairs remain in the session-state list.
544
 */
545
static int tls_verify_chain_pairs_add(request_t *request, fr_tls_session_t *tls_session, fr_tls_conf_t const *conf)
546
0
{
547
0
  X509_STORE_CTX  *x509_ctx = tls_session->validate.x509_ctx;
548
0
  STACK_OF(X509)  *chain = X509_STORE_CTX_get0_chain(x509_ctx);
549
0
  int   untrusted = X509_STORE_CTX_get_num_untrusted(x509_ctx);
550
0
  int   num = sk_X509_num(chain);
551
0
  int   depth;
552
553
  /*
554
   *  Deepest first, so each DER decoded certificate is prepended ahead
555
   *  of its issuer and `DER-Certificate[0]` is the peer's.
556
   */
557
0
  for (depth = num - 1; depth >= 0; depth--) {
558
0
    X509    *cert = sk_X509_value(chain, depth);
559
0
    X509    *issuer;
560
0
    fr_pair_t *container;
561
0
    unsigned int  i;
562
563
0
    if (!verify_applies(conf->verify.attribute_mode, depth, untrusted)) continue;
564
565
    /*
566
     *  Already decoded, the pairs came back with a resumed session.
567
     */
568
0
    container = fr_pair_find_by_da_idx(&request->session_state_pairs, attr_tls_certificate, depth);
569
0
    if (container && !fr_pair_list_empty(&container->vp_group)) continue;
570
571
    /*
572
     *  Build out sufficient TLS-Certificate containers so the
573
     *  TLS-Certificate indexes match the chain depth.
574
     */
575
0
    for (i = fr_pair_count_by_da(&request->session_state_pairs, attr_tls_certificate);
576
0
         i <= (unsigned int)depth;
577
0
         i++) {
578
0
      MEM(container = fr_pair_afrom_da(request->session_state_ctx, attr_tls_certificate));
579
0
      fr_pair_append(&request->session_state_pairs, container);
580
0
    }
581
582
#ifdef STATIC_ANALYZER
583
    /*
584
     *  Container can never be NULL, because if container
585
     *  was previously NULL, i will be <= depth.
586
     */
587
    if (!fr_cond_assert(container)) return -1;
588
#endif
589
590
    /*
591
     *  The issuer is the next certificate up the chain.  The last
592
     *  certificate is its own issuer when it is self-issued, which
593
     *  is what OpenSSL reports as the current issuer when it calls
594
     *  the verify callback for that certificate.
595
     */
596
0
    if ((depth + 1) < num) {
597
0
      issuer = sk_X509_value(chain, depth + 1);
598
0
    } else if (X509_check_issued(cert, cert) == X509_V_OK) {
599
0
      issuer = cert;
600
0
    } else {
601
0
      issuer = NULL;
602
0
    }
603
604
    /*
605
     *  If we fail to populate the cert attributes,
606
     *  trash all instances in the session-state list
607
     *  and cause validation to fail.
608
     */
609
0
    if (fr_tls_session_pairs_from_x509_cert(&container->vp_group, container,
610
0
              request, cert, issuer, conf->verify.der_decode) < 0) {
611
0
      fr_pair_delete_by_da(&request->session_state_pairs, attr_tls_certificate);
612
0
      if (conf->verify.der_decode) {
613
0
        fr_pair_delete_by_da(&request->session_state_pairs, attr_der_certificate);
614
0
      }
615
0
      fr_tls_session_error_add(request, FR_ERROR_VALUE_CERTIFICATE_ATTRIBUTES_FAILED);
616
0
      return -1;
617
0
    }
618
619
0
    log_request_pair(L_DBG_LVL_2, request, NULL, container, "session-state.");
620
0
  }
621
622
0
  return 0;
623
0
}
624
DIAG_ON_OPTIONAL(used-but-marked-unused)
625
626
/** Decode the chain, then push a `verify certificate { ... }` section
627
 *
628
 * tls_session_async_handshake_cont() calls the function once the handshake
629
 * has paused, after the pushes for the session cache and `load psk` sections.
630
 *
631
 * @param[in] request   The current request.
632
 * @param[in] tls_session The current TLS session.
633
 * @return
634
 *  - UNLANG_ACTION_CALCULATE_RESULT  - Nothing to push, resume the handshake.
635
 *  - UNLANG_ACTION_PUSHED_CHILD    - The section is running.
636
 *  - UNLANG_ACTION_FAIL      - The section could not be pushed.
637
 */
638
unlang_action_t fr_tls_verify_cert_pending_push(request_t *request, fr_tls_session_t *tls_session)
639
0
{
640
0
  fr_tls_conf_t *conf = tls_session->conf;
641
642
0
  if (tls_session->validate.state != FR_TLS_VALIDATION_REQUESTED) return UNLANG_ACTION_CALCULATE_RESULT;
643
644
  /*
645
   *  A resumed session has no chain, its pairs came back with the session.
646
   */
647
0
  if (!tls_session->validate.resumed) {
648
0
    RDEBUG2("(TLS) Decoding certificate chain");
649
0
    if (tls_verify_chain_pairs_add(request, tls_session, conf) < 0) {
650
0
      tls_session->validate.state = FR_TLS_VALIDATION_FAILED;
651
0
      return UNLANG_ACTION_CALCULATE_RESULT;
652
0
    }
653
0
  }
654
655
  /*
656
   *  Pairs were all that was wanted.
657
   */
658
0
  if (!conf->verify_certificate || !tls_session->verify_peer_cert) {
659
0
    tls_session->validate.state = FR_TLS_VALIDATION_SUCCESS;
660
0
    return UNLANG_ACTION_CALCULATE_RESULT;
661
0
  }
662
663
0
  RDEBUG2("(TLS) Requesting certificate validation");
664
665
0
  return tls_verify_peer_cert_push(request, tls_session);
666
0
}
667
#endif /* WITH_TLS */