The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
pairs.c
Go to the documentation of this file.
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: a6c32b82b8630d742eff414e08601db0195743d0 $
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: a6c32b82b8630d742eff414e08601db0195743d0 $")
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 
36 #include "attrs.h"
37 #include "base.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 
47 DIAG_OFF(used-but-marked-unused) /* fix spurious warnings for sk macros */
48 /** Extract attributes from an X509 certificate
49  *
50  * @param[out] pair_list to copy attributes to.
51  * @param[in] ctx to allocate attributes in.
52  * @param[in] request the current request.
53  * @param[in] cert to validate.
54  * @return
55  * - 1 already exists.
56  * - 0 on success.
57  * - < 0 on failure.
58  */
59 int fr_tls_session_pairs_from_x509_cert(fr_pair_list_t *pair_list, TALLOC_CTX *ctx, request_t *request, X509 *cert)
60 {
61  int loc;
62  char buff[1024];
63 
64  ASN1_TIME const *asn_time;
65  time_t time;
66 
67  STACK_OF(X509_EXTENSION) const *ext_list = NULL;
68 
69  fr_pair_t *vp = NULL;
70  ssize_t slen;
71 
72  /*
73  * Subject
74  */
76  if (unlikely(X509_NAME_print_ex(fr_tls_bio_dbuff_thread_local(vp, 256, 0),
77  X509_get_subject_name(cert), 0, XN_FLAG_ONELINE) < 0)) {
78  fr_tls_bio_dbuff_thread_local_clear();
79  fr_tls_log(request, "Failed retrieving certificate subject");
80  error:
82  return -1;
83  }
84  fr_pair_value_bstrdup_buffer_shallow(vp, fr_tls_bio_dbuff_thread_local_finalise_bstr(), true);
85 
86  RDEBUG3("Creating attributes for \"%pV\":", fr_box_strvalue_buffer(vp->vp_strvalue));
87 
88  /*
89  * Common name
90  */
91  slen = X509_NAME_get_text_by_NID(X509_get_subject_name(cert),
92  NID_commonName, NULL, 0);
93  if (slen > 0) {
94  char *cn;
95 
97  MEM(fr_pair_value_bstr_alloc(vp, &cn, (size_t)slen, true) == 0); /* Allocs \0 byte in addition to len */
98 
99  slen = X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, cn, (size_t)slen + 1);
100  if (slen < 0) {
101  fr_tls_log(request, "Failed retrieving certificate common name");
102  goto error;
103  }
104  }
105 
106  /*
107  * Signature
108  */
109  {
110  ASN1_BIT_STRING const *sig;
111  X509_ALGOR const *alg;
112 
113  X509_get0_signature(&sig, &alg, cert);
114 
117  (uint8_t const *)ASN1_STRING_get0_data(sig),
118  ASN1_STRING_length(sig), true) == 0);
119 
120  OBJ_obj2txt(buff, sizeof(buff), alg->algorithm, 0);
122  fr_pair_value_strdup(vp, buff, false);
123  }
124 
125  /*
126  * Issuer
127  */
129  if (unlikely(X509_NAME_print_ex(fr_tls_bio_dbuff_thread_local(vp, 256, 0),
130  X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE) < 0)) {
131  fr_tls_bio_dbuff_thread_local_clear();
132  fr_tls_log(request, "Failed retrieving certificate issuer");
133  goto error;
134  }
135  fr_pair_value_bstrdup_buffer_shallow(vp, fr_tls_bio_dbuff_thread_local_finalise_bstr(), true);
136 
137  /*
138  * Serial number
139  */
140  {
141  ASN1_INTEGER const *serial = NULL;
142 
143  serial = X509_get0_serialNumber(cert);
144  if (!serial) {
145  fr_tls_log(request, "Failed retrieving certificate serial");
146  goto error;
147  }
148 
150  MEM(fr_pair_value_memdup(vp, serial->data, serial->length, true) == 0);
151  }
152 
153  /*
154  * Not valid before
155  */
156  asn_time = X509_get0_notBefore(cert);
157 
158  if (fr_tls_utils_asn1time_to_epoch(&time, asn_time) < 0) {
159  RPWDEBUG("Failed parsing certificate not-before");
160  goto error;
161  }
162 
164  vp->vp_date = fr_unix_time_from_time(time);
165 
166  /*
167  * Not valid after
168  */
169  asn_time = X509_get0_notAfter(cert);
170 
171  if (fr_tls_utils_asn1time_to_epoch(&time, asn_time) < 0) {
172  RPWDEBUG("Failed parsing certificate not-after");
173  goto error;
174  }
175 
177  vp->vp_date = fr_unix_time_from_time(time);
178 
179  /*
180  * Get the RFC822 Subject Alternative Name
181  */
182  loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, 0);
183  if (loc >= 0) {
184  X509_EXTENSION *ext = NULL;
185  GENERAL_NAMES *names = NULL;
186  int i;
187 
188  ext = X509_get_ext(cert, loc);
189  if (!ext || !(names = X509V3_EXT_d2i(ext))) goto skip_alt;
190 
191 
192  for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
193  GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
194 
195  switch (name->type) {
196 #ifdef GEN_EMAI
197  case GEN_EMAIL:
201  (char const *)ASN1_STRING_get0_data(name->d.rfc822Name),
202  ASN1_STRING_length(name->d.rfc822Name), true) == 0);
203  break;
204 #endif /* GEN_EMAIL */
205 #ifdef GEN_DNS
206  case GEN_DNS:
210  (char const *)ASN1_STRING_get0_data(name->d.dNSName),
211  ASN1_STRING_length(name->d.dNSName), true) == 0);
212  break;
213 #endif /* GEN_DNS */
214 #ifdef GEN_OTHERNAME
215  case GEN_OTHERNAME:
216  /* look for a MS UPN */
217  if (NID_ms_upn != OBJ_obj2nid(name->d.otherName->type_id)) break;
218 
219  /* we've got a UPN - Must be ASN1-encoded UTF8 string */
220  if (name->d.otherName->value->type == V_ASN1_UTF8STRING) {
224  (char const *)ASN1_STRING_get0_data(name->d.otherName->value->value.utf8string),
225  ASN1_STRING_length(name->d.otherName->value->value.utf8string),
226  true) == 0);
227  break;
228  }
229  RWARN("Invalid UPN in Subject Alt Name (should be UTF-8)");
230  break;
231 #endif /* GEN_OTHERNAME */
232  default:
233  /* XXX TODO handle other SAN types */
234  break;
235  }
236  }
237  if (names != NULL) GENERAL_NAMES_free(names);
238  }
239 
240 skip_alt:
241  /*
242  * Only add extensions for the actual client certificate
243  */
244  ext_list = X509_get0_extensions(cert);
245  if (unlikely(!ext_list)) {
246  RWDEBUG("Failed retrieving extensions");
247  goto done;
248  }
249 
250  /*
251  * Grab the X509 extensions, and create attributes out of them.
252  * For laziness, we reuse the OpenSSL names
253  */
254  if (sk_X509_EXTENSION_num(ext_list) > 0) {
255  int i;
256  BIO *bio;
257  fr_tls_bio_dbuff_t *bd;
258  fr_dbuff_t *in, *out;
259 
260  bio = fr_tls_bio_dbuff_alloc(&bd, NULL, NULL, 257, 4097, true);
261  in = fr_tls_bio_dbuff_in(bd);
262  out = fr_tls_bio_dbuff_out(bd);
263 
264  for (i = 0; i < sk_X509_EXTENSION_num(ext_list); i++) {
265  ASN1_OBJECT *obj;
266  X509_EXTENSION *ext;
267  fr_dict_attr_t const *da;
268  char *p;
269 
270  ext = sk_X509_EXTENSION_value(ext_list, i);
271 
272  obj = X509_EXTENSION_get_object(ext);
273  if (i2a_ASN1_OBJECT(bio, obj) <= 0) {
274  RPWDEBUG("Skipping X509 Extension (%i) conversion to attribute. "
275  "Conversion from ASN1 failed...", i);
276  again:
277  fr_tls_bio_dbuff_reset(bd);
278  continue;
279  }
280 
281  if (fr_dbuff_remaining(out) == 0) goto again; /* Nothing written ? */
282 
283  /*
284  * All disallowed chars get mashed to '-'
285  */
286  for (p = (char *)fr_dbuff_current(out);
287  p < (char *)fr_dbuff_end(out);
288  p++) if (!fr_dict_attr_allowed_chars[(uint8_t)*p]) *p = '-';
289 
290  /*
291  * Terminate the buffer (after char replacement,
292  * so we do don't replace the \0)
293  */
294  if (unlikely(fr_dbuff_in_bytes(in, (uint8_t)'\0') <= 0)) {
295  RWDEBUG("Attribute name too long");
296  goto again;
297  }
298 
300 
301  fr_dbuff_set(in, fr_dbuff_current(in) - 1); /* Ensure the \0 isn't counted in remaining */
302 
303  if (!da) {
304  RWDEBUG3("Skipping attribute \"%pV\": "
305  "Add a dictionary definition if you want to access it",
308  fr_strerror_clear(); /* Don't leave spurious errors from failed resolution */
309  goto again;
310  }
311 
312  fr_tls_bio_dbuff_reset(bd); /* 'free' any data used */
313 
314  X509V3_EXT_print(bio, ext, 0, 0);
315 
316  MEM(vp = fr_pair_afrom_da(ctx, da));
318  NULL, true) < 0) {
319  RPWDEBUG3("Skipping: %s += \"%pV\"",
320  da->name, fr_box_strvalue_len((char *)fr_dbuff_current(out),
322  talloc_free(vp);
323  goto again;
324  }
325  fr_tls_bio_dbuff_reset(bd); /* 'free' any data used */
326 
328  }
329  talloc_free(bd);
330  }
331 
332 done:
333  return 0;
334 }
335 DIAG_ON(used-but-marked-unused)
337 #endif
#define USES_APPLE_DEPRECATED_API
Definition: build.h:431
#define RCSID(id)
Definition: build.h:444
#define DIAG_UNKNOWN_PRAGMAS
Definition: build.h:417
#define DIAG_ON(_x)
Definition: build.h:419
#define unlikely(_x)
Definition: build.h:378
#define DIAG_OFF(_x)
Definition: build.h:418
#define fr_dbuff_current(_dbuff_or_marker)
Return the 'current' position of a dbuff or marker.
Definition: dbuff.h:906
#define fr_dbuff_end(_dbuff_or_marker)
Return the current 'end' position of a dbuff or marker.
Definition: dbuff.h:933
#define fr_dbuff_remaining(_dbuff_or_marker)
Return the number of bytes remaining between the dbuff or marker and the end of the buffer.
Definition: dbuff.h:738
#define fr_dbuff_in_bytes(_dbuff_or_marker,...)
Copy a byte sequence into a dbuff or marker.
Definition: dbuff.h:1460
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition: dict_util.c:2860
bool const fr_dict_attr_allowed_chars[UINT8_MAX+1]
Characters that are allowed in dictionary attribute names.
Definition: dict_util.c:45
static fr_slen_t in
Definition: dict.h:645
HIDDEN fr_dict_attr_t const * attr_tls_certificate
Attribute definitions for lib curl.
Definition: base.c:35
#define RWDEBUG(fmt,...)
Definition: log.h:361
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RWARN(fmt,...)
Definition: log.h:297
#define RPWDEBUG3(fmt,...)
Definition: log.h:368
#define RPWDEBUG(fmt,...)
Definition: log.h:366
#define RWDEBUG3(fmt,...)
Definition: log.h:363
HIDDEN fr_dict_attr_t const * attr_tls_certificate_subject
HIDDEN fr_dict_attr_t const * attr_tls_certificate_serial
HIDDEN fr_dict_attr_t const * attr_tls_certificate_subject_alt_name_dns
HIDDEN fr_dict_attr_t const * attr_tls_certificate_not_after
HIDDEN fr_dict_attr_t const * attr_tls_certificate_not_before
HIDDEN fr_dict_attr_t const * attr_tls_certificate_signature_algorithm
HIDDEN fr_dict_attr_t const * attr_tls_certificate_subject_alt_name_upn
HIDDEN fr_dict_attr_t const * attr_tls_certificate_common_name
HIDDEN fr_dict_attr_t const * attr_tls_certificate_issuer
HIDDEN fr_dict_attr_t const * attr_tls_certificate_signature
HIDDEN fr_dict_attr_t const * attr_tls_certificate_subject_alt_name_email
talloc_free(reap)
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
static size_t used
int fr_pair_append_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
Alloc a new fr_pair_t (and append)
Definition: pair.c:1461
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition: pair.c:278
int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Copy data into an "octets" data type.
Definition: pair.c:2978
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition: pair.c:2631
int fr_pair_value_bstrdup_buffer_shallow(fr_pair_t *vp, char const *src, bool tainted)
Assign a string to a "string" type value pair.
Definition: pair.c:2852
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition: pair.c:1340
int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Copy data into a "string" type value pair.
Definition: pair.c:2781
int fr_pair_value_bstr_alloc(fr_pair_t *vp, char **out, size_t size, bool tainted)
Pre-allocate a memory buffer for a "string" type value pair.
Definition: pair.c:2727
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, bool tainted)
Convert string value to native attribute value.
Definition: pair.c:2586
static fr_bio_t * bio
Definition: radclient-ng.c:86
static bool done
Definition: radclient.c:80
static char const * name
static char buff[sizeof("18446744073709551615")+3]
Definition: size_tests.c:41
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
static const char * names[8]
Definition: time.c:617
static fr_unix_time_t fr_unix_time_from_time(time_t time)
Convert a time_t into out internal fr_unix_time_t.
Definition: time.h:534
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
Definition: pair_inline.c:113
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition: strerror.c:577
int fr_tls_utils_asn1time_to_epoch(time_t *out, ASN1_TIME const *asn1)
Convert OpenSSL's ASN1_TIME to an epoch time.
Definition: utils.c:115
return fr_dbuff_set(dbuff, &our_dbuff)
#define fr_box_strvalue_buffer(_val)
Definition: value.h:282
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:279
static size_t char ** out
Definition: value.h:984