Coverage Report

Created: 2026-09-28 06:27

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