Coverage Report

Created: 2026-08-14 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/tls/pairs.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: 4fa71237698596e98c0b2849f86be943ac2defea $
19
 *
20
 * @file tls/pairs.c
21
 * @brief Functions to convert certificate OIDs to attribute pairs
22
 *
23
 * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24
 */
25
RCSID("$Id: 4fa71237698596e98c0b2849f86be943ac2defea $")
26
USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
27
28
#ifdef WITH_TLS
29
#define LOG_PREFIX "tls"
30
31
#include <freeradius-devel/tls/openssl_user_macros.h>
32
#include <freeradius-devel/util/pair.h>
33
#include <freeradius-devel/server/request.h>
34
#include <freeradius-devel/server/pair.h>
35
#include <freeradius-devel/der/der.h>
36
37
#include "attrs.h"
38
#include "bio.h"
39
#include "log.h"
40
#include "session.h"
41
#include "utils.h"
42
43
#include <openssl/x509v3.h>
44
#include <openssl/ssl.h>
45
46
DIAG_OFF(DIAG_UNKNOWN_PRAGMAS)
47
DIAG_OFF(used-but-marked-unused)  /* fix spurious warnings for sk macros */
48
/** Extract session pairs from the Subject Alternate Name extension
49
 *
50
 */
51
static bool tls_session_pairs_from_san(fr_pair_list_t *pair_list, TALLOC_CTX *ctx, request_t *request, X509_EXTENSION *ext)
52
0
{
53
0
  GENERAL_NAMES *names = NULL;
54
0
  int   i;
55
0
  fr_pair_t *vp;
56
57
0
  if (!(names = X509V3_EXT_d2i(ext))) return false;
58
59
0
  for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
60
0
    GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
61
62
0
    switch (name->type) {
63
0
#ifdef GEN_EMAIL
64
0
    case GEN_EMAIL:
65
0
      MEM(fr_pair_append_by_da(ctx, &vp, pair_list,
66
0
             attr_tls_certificate_subject_alt_name_email) == 0);
67
0
      MEM(fr_pair_value_bstrndup(vp,
68
0
               (char const *)ASN1_STRING_get0_data(name->d.rfc822Name),
69
0
               ASN1_STRING_length(name->d.rfc822Name), true) == 0);
70
0
      break;
71
0
#endif  /* GEN_EMAIL */
72
0
#ifdef GEN_DNS
73
0
    case GEN_DNS:
74
0
      MEM(fr_pair_append_by_da(ctx, &vp, pair_list,
75
0
             attr_tls_certificate_subject_alt_name_dns) == 0);
76
0
      MEM(fr_pair_value_bstrndup(vp,
77
0
               (char const *)ASN1_STRING_get0_data(name->d.dNSName),
78
0
               ASN1_STRING_length(name->d.dNSName), true) == 0);
79
0
      break;
80
0
#endif  /* GEN_DNS */
81
0
#ifdef GEN_OTHERNAME
82
0
    case GEN_OTHERNAME:
83
      /* look for a MS UPN */
84
0
      if (NID_ms_upn != OBJ_obj2nid(name->d.otherName->type_id)) break;
85
86
      /* we've got a UPN - Must be ASN1-encoded UTF8 string */
87
0
      if (name->d.otherName->value->type == V_ASN1_UTF8STRING) {
88
0
        MEM(fr_pair_append_by_da(ctx, &vp, pair_list,
89
0
               attr_tls_certificate_subject_alt_name_upn) == 0);
90
0
        MEM(fr_pair_value_bstrndup(vp,
91
0
                 (char const *)ASN1_STRING_get0_data(name->d.otherName->value->value.utf8string),
92
0
                 ASN1_STRING_length(name->d.otherName->value->value.utf8string),
93
0
                 true) == 0);
94
0
        break;
95
0
      }
96
0
      RWARN("Invalid UPN in Subject Alt Name (should be UTF-8)");
97
0
        break;
98
0
#endif  /* GEN_OTHERNAME */
99
0
      default:
100
        /* XXX TODO handle other SAN types */
101
0
        break;
102
0
    }
103
0
  }
104
0
  if (names != NULL) GENERAL_NAMES_free(names);
105
106
0
  return true;
107
0
}
108
109
/** Extract session pairs from the X509v3-CRL-Distribution-Points extension
110
 *
111
 */
112
static bool tls_session_pairs_from_crl(fr_pair_list_t *pair_list, TALLOC_CTX *ctx, UNUSED request_t *request, X509_EXTENSION *ext)
113
{
114
  ASN1_STRING   *s = X509_EXTENSION_get_data(ext);
115
  char unsigned const *data = ASN1_STRING_get0_data(s);
116
  STACK_OF(DIST_POINT)  *dps;
117
  DIST_POINT    *dp;
118
  STACK_OF(GENERAL_NAME)  *names;
119
  GENERAL_NAME    *name;
120
  fr_pair_t   *vp;
121
  int     i, j;
122
123
  if (!(dps = d2i_CRL_DIST_POINTS(NULL, &data, ASN1_STRING_length(s)))) return false;
124
125
  for (i = 0; i < sk_DIST_POINT_num(dps); i++) {
126
    dp = sk_DIST_POINT_value(dps, i);
127
128
    /*
129
     *  RFC 5280 Section 4.2.1.13 says that the distpoint is optional.
130
     */
131
    if (!dp->distpoint) {
132
      CRL_DIST_POINTS_free(dps);
133
      return false;
134
    }
135
136
    names = dp->distpoint->name.fullname;
137
138
    /*
139
     *  We only want CRL distribution points that cover all reasons,
140
     *  so ignore any where reasons are set.
141
     */
142
    if (dp->reasons) continue;
143
144
    for (j = 0; j < sk_GENERAL_NAME_num(names); j++) {
145
      name = sk_GENERAL_NAME_value(names, j);
146
147
      if (name->type != GEN_URI) continue;
148
      MEM(fr_pair_append_by_da(ctx, &vp, pair_list,
149
             attr_tls_certificate_x509v3_crl_distribution_points) == 0);
150
      MEM(fr_pair_value_strdup(vp,
151
             (char const *)ASN1_STRING_get0_data(name->d.uniformResourceIdentifier),
152
             true) == 0);
153
    }
154
  }
155
156
  CRL_DIST_POINTS_free(dps);
157
158
  return true;
159
}
160
161
/** Extract attributes from an X509 certificate
162
 *
163
 * @param[out] pair_list  to copy attributes to.
164
 * @param[in] ctx   to allocate attributes in.
165
 * @param[in] request   the current request.
166
 * @param[in] cert    to validate.
167
 * @param[in] der_decode  should the certificate be parsed with the DER decoder.
168
 * @return
169
 *  - 1 already exists.
170
 *  - 0 on success.
171
 *  - < 0 on failure.
172
 */
173
int fr_tls_session_pairs_from_x509_cert(fr_pair_list_t *pair_list, TALLOC_CTX *ctx, request_t *request, X509 *cert,
174
#if OPENSSL_VERSION_NUMBER >= 0x30400000L
175
          bool der_decode
176
#else
177
          UNUSED bool der_decode
178
#endif
179
        )
180
0
{
181
0
  int   loc;
182
0
  char    buff[1024];
183
184
0
  ASN1_TIME const *asn_time;
185
0
  time_t    time;
186
187
0
  STACK_OF(X509_EXTENSION) const *ext_list = NULL;
188
189
0
  fr_pair_t *vp = NULL;
190
0
  ssize_t   slen;
191
0
  bool    san_found = false, crl_found = false;
192
193
  /*
194
   *  We require OpenSSL >= 3.4 to call the DER decoder due to the stack size
195
   *  needed to handle the recursive calls involved in certificate decoding.
196
   */
197
#if OPENSSL_VERSION_NUMBER >= 0x30400000L
198
  if (der_decode) {
199
    uint8_t     *cert_der;
200
    uint8_t     *cd;
201
    int     der_len;
202
    fr_der_decode_ctx_t der_ctx;
203
    fr_pair_list_t    tmp_list;
204
205
    der_len = i2d_X509(cert, NULL);
206
    if (der_len < 0) {
207
      fr_tls_log(request, "Failed retrieving certificate");
208
      return -1;
209
    }
210
    der_ctx = (fr_der_decode_ctx_t) {
211
      .tmp_ctx = talloc_new(ctx),
212
      .root = attr_der_certificate,
213
    };
214
    cert_der = cd = talloc_array(der_ctx.tmp_ctx, uint8_t, der_len);
215
    i2d_X509(cert, &cd);
216
    fr_pair_list_init(&tmp_list);
217
    slen = fr_der_decode_pair_dbuff(request->session_state_ctx, &tmp_list,
218
            attr_der_certificate, &FR_DBUFF_TMP(cert_der, (size_t)der_len), &der_ctx);
219
    talloc_free(der_ctx.tmp_ctx);
220
    if (slen < 0) {
221
      fr_tls_log(request, "Failed decoding certificate");
222
      fr_pair_list_free(&tmp_list);
223
      return -1;
224
    }
225
    /*
226
     *  Certificates are decoded in CA .. intermediate .. client sequence
227
     *  so, prepend each decoded one to get the client cert first.
228
     */
229
    fr_pair_list_prepend(&request->session_state_pairs, &tmp_list);
230
  }
231
#endif
232
233
  /*
234
   *  Subject
235
   */
236
0
  MEM(fr_pair_append_by_da(ctx, &vp, pair_list, attr_tls_certificate_subject) == 0);
237
0
  if (unlikely(X509_NAME_print_ex(fr_tls_bio_dbuff_thread_local(vp, 256, 0),
238
0
          X509_get_subject_name(cert), 0, XN_FLAG_ONELINE) < 0)) {
239
0
    fr_tls_bio_dbuff_thread_local_clear();
240
0
    fr_tls_log(request, "Failed retrieving certificate subject");
241
0
  error:
242
0
    fr_pair_list_free(pair_list);
243
0
    return -1;
244
0
  }
245
0
  fr_pair_value_bstrdup_buffer_shallow(vp, fr_tls_bio_dbuff_thread_local_finalise_bstr(), true);
246
247
0
  RDEBUG3("Creating attributes for \"%pV\":", fr_box_strvalue_buffer(vp->vp_strvalue));
248
249
  /*
250
   *  Common name
251
   */
252
0
  slen = X509_NAME_get_text_by_NID(X509_get_subject_name(cert),
253
0
           NID_commonName, NULL, 0);
254
0
  if (slen > 0) {
255
0
    char *cn;
256
257
0
    MEM(fr_pair_append_by_da(ctx, &vp, pair_list, attr_tls_certificate_common_name) == 0);
258
0
    MEM(fr_pair_value_bstr_alloc(vp, &cn, (size_t)slen, true) == 0); /* Allocs \0 byte in addition to len */
259
260
0
    slen = X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, cn, (size_t)slen + 1);
261
0
    if (slen < 0) {
262
0
      fr_tls_log(request, "Failed retrieving certificate common name");
263
0
      goto error;
264
0
    }
265
0
  }
266
267
  /*
268
   *  Signature
269
   */
270
0
  {
271
0
    ASN1_BIT_STRING const *sig;
272
0
    X509_ALGOR const *alg;
273
274
0
    X509_get0_signature(&sig, &alg, cert);
275
276
0
    MEM(fr_pair_append_by_da(ctx, &vp, pair_list, attr_tls_certificate_signature) == 0);
277
0
    MEM(fr_pair_value_memdup(vp,
278
0
           (uint8_t const *)ASN1_STRING_get0_data(sig),
279
0
           ASN1_STRING_length(sig), true) == 0);
280
281
0
    OBJ_obj2txt(buff, sizeof(buff), alg->algorithm, 0);
282
0
    MEM(fr_pair_append_by_da(ctx, &vp, pair_list, attr_tls_certificate_signature_algorithm) == 0);
283
0
    fr_pair_value_strdup(vp, buff, false);
284
0
  }
285
286
  /*
287
   *  Issuer
288
   */
289
0
  MEM(fr_pair_append_by_da(ctx, &vp, pair_list, attr_tls_certificate_issuer) == 0);
290
0
  if (unlikely(X509_NAME_print_ex(fr_tls_bio_dbuff_thread_local(vp, 256, 0),
291
0
          X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE) < 0)) {
292
0
    fr_tls_bio_dbuff_thread_local_clear();
293
0
    fr_tls_log(request, "Failed retrieving certificate issuer");
294
0
    goto error;
295
0
  }
296
0
  fr_pair_value_bstrdup_buffer_shallow(vp, fr_tls_bio_dbuff_thread_local_finalise_bstr(), true);
297
298
  /*
299
   *  Serial number
300
   */
301
0
  {
302
0
    ASN1_INTEGER const *serial = NULL;
303
0
    unsigned char *der;
304
0
    int len;
305
306
0
    serial = X509_get0_serialNumber(cert);
307
0
    if (!serial) {
308
0
      fr_tls_log(request, "Failed retrieving certificate serial");
309
0
      goto error;
310
0
    }
311
312
0
    len = i2d_ASN1_INTEGER(serial, NULL);  /* get length */
313
0
    MEM(fr_pair_append_by_da(ctx, &vp, pair_list, attr_tls_certificate_serial) == 0);
314
0
    MEM(fr_pair_value_mem_alloc(vp, &der, len, false) == 0);
315
0
    i2d_ASN1_INTEGER(serial, &der);
316
0
  }
317
318
  /*
319
   *  Not valid before
320
   */
321
0
  asn_time = X509_get0_notBefore(cert);
322
323
0
  if (fr_tls_utils_asn1time_to_epoch(&time, asn_time) < 0) {
324
0
    RPWDEBUG("Failed parsing certificate not-before");
325
0
    goto error;
326
0
  }
327
328
0
  MEM(fr_pair_append_by_da(ctx, &vp, pair_list, attr_tls_certificate_not_before) == 0);
329
0
  vp->vp_date = fr_unix_time_from_time(time);
330
331
  /*
332
   *  Not valid after
333
   */
334
0
  asn_time = X509_get0_notAfter(cert);
335
336
0
  if (fr_tls_utils_asn1time_to_epoch(&time, asn_time) < 0) {
337
0
    RPWDEBUG("Failed parsing certificate not-after");
338
0
    goto error;
339
0
  }
340
341
0
  MEM(fr_pair_append_by_da(ctx, &vp, pair_list, attr_tls_certificate_not_after) == 0);
342
0
  vp->vp_date = fr_unix_time_from_time(time);
343
344
  /*
345
   *  Get the RFC822 Subject Alternative Name
346
   */
347
0
  loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, 0);
348
0
  if (loc >= 0) {
349
0
    X509_EXTENSION  *ext = X509_get_ext(cert, loc);
350
0
    if (ext) san_found = tls_session_pairs_from_san(pair_list, ctx, request, ext);
351
0
  }
352
353
0
  loc = X509_get_ext_by_NID(cert, NID_crl_distribution_points, 0);
354
0
  if (loc >= 0) {
355
0
    X509_EXTENSION  *ext = X509_get_ext(cert, loc);
356
0
    if (ext) crl_found = tls_session_pairs_from_crl(pair_list, ctx, request, ext);
357
0
  }
358
359
  /*
360
   *  Only add extensions for the actual client certificate
361
   */
362
0
  ext_list = X509_get0_extensions(cert);
363
0
  if (unlikely(!ext_list)) {
364
0
    RWDEBUG("Failed retrieving extensions");
365
0
    goto done;
366
0
  }
367
368
  /*
369
   *  Grab the X509 extensions, and create attributes out of them.
370
   *  For laziness, we reuse the OpenSSL names
371
   */
372
0
  if (sk_X509_EXTENSION_num(ext_list) > 0) {
373
0
    int     i;
374
0
    BIO     *bio;
375
0
    fr_tls_bio_dbuff_t  *bd;
376
0
    fr_dbuff_t    *in, *out;
377
378
0
    bio = fr_tls_bio_dbuff_alloc(&bd, NULL, NULL, 257, 4097, true);
379
0
    in = fr_tls_bio_dbuff_in(bd);
380
0
    out = fr_tls_bio_dbuff_out(bd);
381
382
0
    for (i = 0; i < sk_X509_EXTENSION_num(ext_list); i++) {
383
0
      ASN1_OBJECT   *obj;
384
0
      X509_EXTENSION    *ext;
385
0
      fr_dict_attr_t const  *da;
386
0
      char      *p;
387
388
0
      ext = sk_X509_EXTENSION_value(ext_list, i);
389
390
0
      obj = X509_EXTENSION_get_object(ext);
391
392
      /*
393
       *  If this is extension is Subject Alternate Name have we already
394
       *  handled it?  If not, do that now.
395
       *
396
       *  Some certificate encodings have been observed where the SAN extension
397
       *  was not found by X509_get_ext_by_NID() but then seen when the list
398
       *  of extensions is handled here.
399
       */
400
0
      if (OBJ_obj2nid(obj) == NID_subject_alt_name) {
401
0
        if (!san_found) san_found = tls_session_pairs_from_san(pair_list, ctx, request, ext);
402
0
        goto again;
403
0
      }
404
405
0
      if (OBJ_obj2nid(obj) == NID_crl_distribution_points) {
406
0
        if (!crl_found) crl_found = tls_session_pairs_from_crl(pair_list, ctx, request, ext);
407
0
        goto again;
408
0
      }
409
410
0
      if (i2a_ASN1_OBJECT(bio, obj) <= 0) {
411
0
        RPWDEBUG("Skipping X509 Extension (%i) conversion to attribute. "
412
0
           "Conversion from ASN1 failed...", i);
413
0
      again:
414
0
        fr_tls_bio_dbuff_reset(bd);
415
0
        continue;
416
0
      }
417
418
0
      if (fr_dbuff_remaining(out) == 0) goto again; /* Nothing written ? */
419
420
      /*
421
       *  All disallowed chars get mashed to '-'
422
       */
423
0
      for (p = (char *)fr_dbuff_current(out);
424
0
           p < (char *)fr_dbuff_end(out);
425
0
           p++) if (!fr_dict_attr_allowed_chars[(uint8_t)*p]) *p = '-';
426
427
      /*
428
       *  Terminate the buffer (after char replacement,
429
       *  so we do don't replace the \0)
430
       */
431
0
      if (unlikely(fr_dbuff_in_bytes(in, (uint8_t)'\0') <= 0)) {
432
0
        RWDEBUG("Attribute name too long");
433
0
        goto again;
434
0
      }
435
436
0
      da = fr_dict_attr_by_name(NULL, attr_tls_certificate, (char *)fr_dbuff_current(out));
437
438
0
      fr_dbuff_set(in, fr_dbuff_current(in) - 1); /* Ensure the \0 isn't counted in remaining */
439
440
0
      if (!da) {
441
0
        RWDEBUG3("Skipping attribute \"%pV\": "
442
0
           "Add a dictionary definition if you want to access it",
443
0
           fr_box_strvalue_len((char *)fr_dbuff_current(out),
444
0
                     fr_dbuff_remaining(out)));
445
0
        fr_strerror_clear();  /* Don't leave spurious errors from failed resolution */
446
0
        goto again;
447
0
      }
448
449
0
      fr_tls_bio_dbuff_reset(bd); /* 'free' any data used */
450
451
0
      if (X509V3_EXT_print(bio, ext, X509V3_EXT_PARSE_UNKNOWN, 0) != 1) {
452
0
        REDEBUG("Failed extracting data for \"%s\"", da->name);
453
0
        goto again;
454
0
      }
455
456
0
      MEM(vp = fr_pair_afrom_da(ctx, da));
457
0
      if (fr_pair_value_from_str(vp, (char *)fr_dbuff_current(out), fr_dbuff_remaining(out),
458
0
               NULL, true) < 0) {
459
0
        RPWDEBUG3("Skipping: %s += \"%pV\"",
460
0
            da->name, fr_box_strvalue_len((char *)fr_dbuff_current(out),
461
0
                    fr_dbuff_remaining(out)));
462
0
        talloc_free(vp);
463
0
        goto again;
464
0
      }
465
0
      fr_tls_bio_dbuff_reset(bd); /* 'free' any data used */
466
467
0
      fr_pair_append(pair_list, vp);
468
0
    }
469
0
    talloc_free(bd);
470
0
  }
471
472
0
done:
473
0
  return 0;
474
0
}
475
476
static int fr_tls_extension_decode(request_t *request, fr_pair_t *container, uint8_t const *extension,
477
           size_t hlen, size_t dlen, size_t total_len, fr_dict_attr_t const *da)
478
0
{
479
0
  size_t i,extension_len;
480
0
  uint8_t const *data;
481
482
0
  fr_assert((hlen == 1) || (hlen == 2));
483
0
  fr_assert((dlen == 1) || (dlen == 2));
484
485
0
  if (total_len < hlen) {
486
0
    REDEBUG("Missing length in %s extension", da->name);
487
0
    return -1;
488
0
  }
489
490
0
  if (hlen == 1) {
491
0
    fr_assert(da->type == FR_TYPE_UINT8);
492
0
    extension_len = extension[0];
493
0
  } else {
494
0
    fr_assert(da->type == FR_TYPE_UINT16);
495
0
    extension_len = (extension[0] << 8) + extension[1];
496
0
  }
497
498
0
  if (extension_len * dlen + hlen > total_len) {
499
0
    REDEBUG("Invalid length in %s extension", da->name);
500
0
    return -1;
501
0
  }
502
503
0
  data = extension + hlen;
504
505
0
  for (i = 0; i < extension_len; i += dlen) {
506
0
    fr_pair_t *vp;
507
508
0
    MEM(fr_pair_append_by_da(container, &vp, &container->vp_group, da) >= 0);
509
510
0
    switch (dlen) {
511
0
    case 1:
512
0
      vp->vp_uint8= data[0];
513
0
      break;
514
515
0
    case 2:
516
0
      vp->vp_uint16 = (data[0] << 8) + data[1];
517
0
      break;
518
0
    }
519
520
0
    data += dlen;
521
0
  }
522
523
0
  return 0;
524
0
}
525
526
/** Callback to extract pairs from a Client Hello
527
 *
528
 */
529
int fr_tls_session_client_hello_cb(SSL *ssl, UNUSED int *al, UNUSED void *arg)
530
0
{
531
0
  request_t   *request = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST);
532
0
  request_t   *parent = request->parent;
533
0
  uint8_t const   *ciphers, *extension;
534
0
  int     i, *extensions = NULL;
535
0
  size_t      data_size, j;
536
0
  STACK_OF(SSL_CIPHER)  *sk;
537
0
  SSL_CIPHER const  *cipher;
538
0
  STACK_OF(SSL_CIPHER)  *scsvs;
539
0
  fr_pair_t   *container, *vp;
540
541
0
  fr_assert(parent);
542
543
0
  container = fr_pair_find_by_da(&parent->session_state_pairs, NULL, attr_tls_client_hello);
544
0
  if (container) return SSL_CLIENT_HELLO_SUCCESS;
545
546
0
  MEM(container = fr_pair_afrom_da(parent->session_state_ctx, attr_tls_client_hello));
547
548
0
  data_size = SSL_client_hello_get0_ciphers(ssl, &ciphers);
549
0
  if (SSL_bytes_to_cipher_list(ssl, ciphers, data_size, SSL_client_hello_isv2(ssl), &sk, &scsvs) == 0) {
550
0
    RPEDEBUG("Failed to decode cipher list");
551
0
  fail:
552
0
    if (extensions) OPENSSL_free(extensions);
553
0
    talloc_free(container);
554
0
    return SSL_CLIENT_HELLO_ERROR;
555
0
  }
556
557
0
  for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
558
0
    fr_pair_append_by_da(container, &vp, &container->vp_group, attr_tls_client_hello_cipher);
559
0
    cipher = sk_SSL_CIPHER_value(sk, i);
560
0
    fr_value_box_strdup(vp, &vp->data, NULL, SSL_CIPHER_get_name(cipher), false);
561
0
  }
562
563
0
  sk_SSL_CIPHER_free(sk);
564
0
  sk_SSL_CIPHER_free(scsvs);
565
566
  /*
567
   *  Fetch the extensions and decode the ones we know about
568
   *  These are the ones which are returned as simple lists
569
   *  of values of known length which map to enum attributes.
570
   */
571
0
  if (SSL_client_hello_get1_extensions_present(ssl, &extensions, &data_size) == 0) {
572
0
    RPEDEBUG("Failed to fetch client hello extensions");
573
0
    goto fail;
574
0
  }
575
576
0
  for (j = 0; j < data_size; j++) {
577
0
    size_t total_len;
578
579
0
    if (SSL_client_hello_get0_ext(ssl, extensions[j], &extension, &total_len) == 0) {
580
0
      RPDEBUG("Failed getting client hello extension %d", extensions[j]);
581
0
      goto fail;
582
0
    }
583
584
0
    switch (extensions[j]) {
585
0
    case TLSEXT_TYPE_supported_groups: /* length[2] + 2*data */
586
0
      if (fr_tls_extension_decode(request, container, extension, 2, 2, total_len,
587
0
                attr_tls_client_hello_supported_group) < 0) goto fail;
588
0
      break;
589
590
0
    case TLSEXT_TYPE_ec_point_formats: /* length[1] + data */
591
0
      if (fr_tls_extension_decode(request, container, extension, 1, 1, total_len,
592
0
                attr_tls_client_hello_ec_point_format) < 0) goto fail;
593
0
      break;
594
595
0
    case TLSEXT_TYPE_signature_algorithms: /* length[2] + 2*data */
596
0
      if (fr_tls_extension_decode(request, container, extension, 2, 2, total_len,
597
0
                attr_tls_client_hello_sig_algo) < 0) goto fail;
598
0
      break;
599
600
0
    case TLSEXT_TYPE_supported_versions: /* length[1] + 2*data */
601
0
      if (fr_tls_extension_decode(request, container, extension, 1, 2, total_len,
602
0
                attr_tls_client_hello_tls_version) < 0) goto fail;
603
0
      break;
604
605
0
    case TLSEXT_TYPE_psk_kex_modes: /* length[1] + data */
606
0
      if (fr_tls_extension_decode(request, container, extension, 1, 1, total_len,
607
0
                attr_tls_client_hello_psk_key_mode) < 0) goto fail;
608
0
      break;
609
0
    }
610
0
  }
611
612
0
  if (extensions) OPENSSL_free(extensions);
613
614
  /*
615
   *  This is the version being negotiated by the client, but tops at 1.2.
616
   *  After that the supported_versions extension is where the real value is.
617
   */
618
0
  fr_pair_append_by_da(container, &vp, &container->vp_group, attr_tls_client_hello_tls_version);
619
0
  vp->vp_uint16 = SSL_client_hello_get0_legacy_version(ssl);
620
621
0
  fr_pair_append(&parent->session_state_pairs, container);
622
623
  return SSL_CLIENT_HELLO_SUCCESS;
624
0
}
625
DIAG_ON(used-but-marked-unused)
626
DIAG_ON(DIAG_UNKNOWN_PRAGMAS)
627
#endif