The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
verify.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: 3903553b74c9ef9660c976a43e0ced349d8a1525 $
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 #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/interpret.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 "attrs.h"
42 #include "base.h"
43 
44 /** Check to see if a verification operation should apply to a certificate
45  *
46  * @param[in] depth starting at 0.
47  * Certificate 0 is the leaf cert (i.e. the client or server cert);
48  * @param[in] untrusted The number of untrusted certificates.
49  * @param[in] mode to check
50  * @return
51  * - true if a given validation check should apply.
52  ** - false if a validation check should not apply.
53  */
54 static inline CC_HINT(always_inline)
55 bool verify_applies(fr_tls_verify_mode_t mode, int depth, int untrusted)
56 {
57  if (mode == FR_TLS_VERIFY_MODE_ALL) return true;
58  if (mode == FR_TLS_VERIFY_MODE_DISABLED) return false;
59 
60  if ((mode & FR_TLS_VERIFY_MODE_LEAF) && (depth == 0)) return true;
61  if ((mode & FR_TLS_VERIFY_MODE_ISSUER) && (depth == 1)) return true;
62  if ((mode & FR_TLS_VERIFY_MODE_UNTRUSTED) && (depth < untrusted)) return true;
63 
64  return false;
65 }
66 
68 DIAG_OFF(used-but-marked-unused) /* fix spurious warnings for sk macros */
69 
70 /** Print verbose humanly readable messages about why certificate validation failed
71  *
72  */
73 static void tls_verify_error_detail(request_t *request, SSL_CTX *ctx, int err)
74 {
75  X509_STORE *store = SSL_CTX_get_ex_data(ctx, FR_TLS_EX_CTX_INDEX_VERIFY_STORE);
76 
77  switch (err) {
78  /*
79  * We linked the provided cert to at least one
80  * other in a chain, but the chain doesn't terminate
81  * in a root CA.
82  */
83  case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
85 
86  /*
87  * We failed to link the provided cert to any
88  * other local certificates in the chain.
89  */
90  case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
91  RDEBUG2("Static certificates in verification store are");
92  if (RDEBUG_ENABLED2) {
93  RINDENT();
94  fr_tls_x509_objects_log(request, L_DBG, X509_STORE_get0_objects(store));
95  REXDENT();
96  }
97  break;
98 
99  default:
100  break;
101  }
102 }
103 
104 /** Validates a certificate using custom logic
105  *
106  * Before trusting a certificate, we make sure that the certificate is
107  * 'valid'. There are several checks we perform to verify its validity.
108  *
109  * 1. Verify the certificate's signature, and verifying that the certificate has
110  * been issued by a trusted Certificate Authority (this is done for us by OpenSSL).
111  *
112  * 2. Verify that the certificate is valid for the present date (i.e. it is being
113  * presented within its validity dates).
114  *
115  * 3. Verify that the certificate has not been revoked by its issuing Certificate
116  * Authority, by checking with respect to a Certificate Revocation List (CRL).
117  *
118  * @note This callback will be called multiple times based on the depth of the root
119  * certificate chain.
120  *
121  * @note As a byproduct of validation, various OIDs will be extracted from the
122  * certificates, and inserted into the session-state list as fr_pair_t.
123  *
124  * @param ok preverify ok. 1 if true, 0 if false.
125  * @param x509_ctx containing certs to verify.
126  * @return
127  * - 0 if not valid.
128  * - 1 if valid.
129  */
130 int fr_tls_verify_cert_cb(int ok, X509_STORE_CTX *x509_ctx)
131 {
132  X509 *cert;
133 
134  SSL_CTX *ssl_ctx;
135  SSL *ssl;
136  fr_tls_session_t *tls_session;
137  int err, depth;
138  fr_tls_conf_t *conf;
139  int my_ok = ok;
140  int untrusted;
141 
142  request_t *request;
143  fr_pair_t *container = NULL;
144 
145  cert = X509_STORE_CTX_get_current_cert(x509_ctx);
146  err = X509_STORE_CTX_get_error(x509_ctx);
147  depth = X509_STORE_CTX_get_error_depth(x509_ctx);
148  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  ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
155  ssl_ctx = SSL_get_SSL_CTX(ssl);
156  conf = fr_tls_session_conf(ssl);
157  tls_session = talloc_get_type_abort(SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_TLS_SESSION), fr_tls_session_t);
158  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  if (!tls_session->can_pause) {
166  fr_assert_msg("Unexpected call to %s. "
167  "tls_session_async_handshake_cont must be in call stack", __FUNCTION__);
168  return 0;
169  }
170 
171  /*
172  * Bail out as quickly as possible, producing
173  * as few errors as possible.
174  */
175  if (unlang_request_is_cancelled(request)) {
176  X509_STORE_CTX_set_error(x509_ctx, 0);
177  return 1;
178  }
179 
180  if (RDEBUG_ENABLED3) {
181  char subject[2048];
182  STACK_OF(X509) *our_chain;
183  int i;
184 
185 #if OPENSSL_VERSION_NUMBER >= 0x10101000L
186  our_chain = X509_STORE_CTX_get0_chain(x509_ctx);
187 #else
188  our_chain = X509_STORE_CTX_get_chain(x509_ctx);
189 #endif
190 
191  RDEBUG3("Certificate chain - %i cert(s) untrusted", untrusted);
192  for (i = sk_X509_num(our_chain); i > 0 ; i--) {
193  X509 *this_cert = sk_X509_value(our_chain, i - 1);
194 
195  X509_NAME_oneline(X509_get_subject_name(this_cert), subject, sizeof(subject));
196  subject[sizeof(subject) - 1] = '\0';
197 
198  RDEBUG3("%s [%i] %s", this_cert == cert ? ">" : " ", i - 1, subject);
199  }
200  }
201 
202  /*
203  * See if the user has disabled verification for
204  * this certificate. If they have, force verification
205  * to succeed.
206  */
207  if (!my_ok) {
208  char const *p = X509_verify_cert_error_string(err);
209  if (!verify_applies(conf->verify.mode, depth, untrusted) ||
210  ((conf->verify.allow_expired_crl) && (err == X509_V_ERR_CRL_HAS_EXPIRED)) ||
211  ((conf->verify.allow_not_yet_valid_crl) && (err == X509_V_ERR_CRL_NOT_YET_VALID))) {
212  RDEBUG2("Ignoring verification error - %s (%i)", p, err);
213  tls_verify_error_detail(request, ssl_ctx, err);
214 
215  my_ok = 1;
216  X509_STORE_CTX_set_error(x509_ctx, 0);
217  } else {
218  RERROR("Verification error - %s (%i)", p, err);
219  tls_verify_error_detail(request, ssl_ctx, err);
220  goto done;
221  }
222  }
223 
224  if (verify_applies(conf->verify.attribute_mode, depth, untrusted) &&
225  (!(container = fr_pair_find_by_da_idx(&request->session_state_pairs, attr_tls_certificate, depth)) ||
226  fr_pair_list_empty(&container->vp_group))) {
227  if (!container) {
228  unsigned int i;
229 
230  /*
231  * Build a stack of container attributes.
232  *
233  * OpenSSL passes us the deepest certificate
234  * first, so we need to build out sufficient
235  * TLS-Certificate container TLVs so the TLS-Certificate
236  * indexes match the attribute depth.
237  */
238  for (i = fr_pair_count_by_da(&request->session_state_pairs, attr_tls_certificate);
239  i <= (unsigned int)depth;
240  i++) {
241  MEM(container = fr_pair_afrom_da(request->session_state_ctx, attr_tls_certificate));
242  fr_pair_append(&request->session_state_pairs, container);
243  }
244  }
245 
246 #ifdef STATIC_ANALYZER
247  /*
248  * Container can never be NULL, because if container
249  * was previously NULL, i will be <= depth.
250  */
251  if (!fr_cond_assert(container)) {
252  my_ok = 0;
253  goto done;
254  }
255 #endif
256  /*
257  * If we fail to populate the cert attributes,
258  * trash all instances in the session-state list
259  * and cause validation to fail.
260  */
261  if (fr_tls_session_pairs_from_x509_cert(&container->vp_group, container,
262  request, cert) < 0) {
263  fr_pair_delete_by_da(&request->session_state_pairs, attr_tls_certificate);
264  my_ok = 0;
265  goto done;
266  }
267 
268  log_request_pair(L_DBG_LVL_2, request, NULL, container, "&session-state.");
269  }
270 done:
271  /*
272  * If verification hasn't already failed
273  * and we're meant to verify this cert
274  * then call the virtual server.
275  *
276  * We only call the virtual server for
277  * the certificate at depth 0 as all
278  * other certificate attributes should
279  * have been added by this point.
280  */
281  if (my_ok && (depth == 0)) {
282  if (conf->virtual_server && tls_session->verify_client_cert) {
283  RDEBUG2("Requesting certificate validation");
284 
285  /*
286  * This sets the validation state of the tls_session
287  * so that when we call ASYNC_pause_job(), and execution
288  * jumps back to tls_session_async_handshake_cont
289  * (just under SSL_read())
290  * the code there knows what job it needs to push onto
291  * the unlang stack.
292  */
293  fr_tls_verify_cert_request(tls_session, SSL_session_reused(tls_session->ssl));
294 
295  /*
296  * Jumps back to SSL_read() in session.c
297  *
298  * Be aware that if the request is cancelled
299  * whatever was meant to be done during the
300  * time we yielded may not have been completed.
301  */
302  ASYNC_pause_job();
303 
304  /*
305  * Just try and bail out as quickly as possible.
306  */
307  if (unlang_request_is_cancelled(request)) {
308  X509_STORE_CTX_set_error(x509_ctx, 0);
309  fr_tls_verify_cert_reset(tls_session);
310  return 1;
311  }
312 
313 
314  /*
315  * If we couldn't validate the certificate
316  * then validation overall fails.
317  */
318  if (!fr_tls_verify_cert_result(tls_session)) {
319  REDEBUG("Certificate validation failed");
320  my_ok = 0;
321  X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
322  }
323  }
324  }
325 
326  tls_session->client_cert_ok = (my_ok > 0);
327  RDEBUG2("[verify] = %s", my_ok ? "ok" : "invalid");
328 
329  return my_ok;
330 }
331 DIAG_ON(used-but-marked-unused)
333 
334 /** Revalidates the client's certificate chain
335  *
336  * Wraps the fr_tls_verify_cert_cb callback, allowing us to use the same
337  * validation logic whenever we need to.
338  *
339  * @note Only use so far is forcing the chain to be re-validated on session
340  * resumption.
341  *
342  * @return
343  * - 1 if the chain could be validated.
344  * - 0 if the chain failed validation.
345  */
346 int fr_tls_verify_cert_chain(request_t *request, SSL *ssl)
347 {
348  int err;
349  int verify;
350  int ret = 1;
351 
352  SSL_CTX *ssl_ctx;
353  STACK_OF(X509) *chain;
354  X509 *cert;
355  X509_STORE *store;
356  X509_STORE_CTX *store_ctx;
357 
358  /*
359  * If there's no client certificate, we just return OK.
360  */
361 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
362  cert = SSL_get0_peer_certificate(ssl); /* Does not increase ref count */
363 #else
364  cert = SSL_get_peer_certificate(ssl); /* Increases ref count */
365 #endif
366  if (!cert) return 1;
367 
368  ssl_ctx = SSL_get_SSL_CTX(ssl);
369  store_ctx = X509_STORE_CTX_new();
370  chain = SSL_get_peer_cert_chain(ssl); /* Does not increase ref count */
371  store = SSL_CTX_get_ex_data(ssl_ctx, FR_TLS_EX_CTX_INDEX_VERIFY_STORE); /* Gets the verification store */
372 
373  /*
374  * This sets up a store_ctx for doing peer certificate verification.
375  *
376  * store_ctx - Is the ctx to initialise
377  * store - Is an X509_STORE of implicitly
378  * trusted certificates. Here we're using
379  * the verify store that was created when we
380  * allocated the SSL_CTX.
381  * cert - Is the certificate to validate.
382  * chain - Is any other certificates the peer provided
383  * us in order to build a chain from a trusted
384  * root or intermediary to its leaf (cert).
385  *
386  * Note: SSL_CTX_get_cert_store() returns the ctx->cert_store, which
387  * is not the same as the verification cert store.
388  */
389  X509_STORE_CTX_init(store_ctx, store, cert, chain);
390  X509_STORE_CTX_set_ex_data(store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx(), ssl);
391  X509_STORE_CTX_set_verify_cb(store_ctx, fr_tls_verify_cert_cb);
392 
393  verify = X509_verify_cert(store_ctx);
394  if (verify != 1) {
395  err = X509_STORE_CTX_get_error(store_ctx);
396 
397  if (err != X509_V_OK) {
398  REDEBUG("Failed re-validating resumed session: %s", X509_verify_cert_error_string(err));
399  ret = 0;
400  }
401  }
402 
403 #if OPENSSL_VERSION_NUMBER < 0x30000000L
404  X509_free(cert);
405 #endif
406  X509_STORE_CTX_free(store_ctx);
407 
408  return ret;
409 }
410 
411 /** Process the result of `verify certificate { ... }`
412  *
413  */
414 static unlang_action_t tls_verify_client_cert_result(UNUSED rlm_rcode_t *p_result, UNUSED int *priority,
415  request_t *request, void *uctx)
416 {
417  fr_tls_session_t *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t);
418  fr_pair_t *vp;
419 
420  fr_assert(tls_session->validate.state == FR_TLS_VALIDATION_REQUESTED);
421 
422  vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_tls_packet_type);
423  if (!vp || (vp->vp_uint32 != enum_tls_packet_type_success->vb_uint32)) {
424  REDEBUG("Failed (re-)validating certificates");
425  tls_session->validate.state = FR_TLS_VALIDATION_FAILED;
427  }
428 
429  tls_session->validate.state = FR_TLS_VALIDATION_SUCCESS;
430 
431  RDEBUG2("Certificates (re-)validated");
432 
434 }
435 
436 /** Push a `verify certificate { ... }` call into the current request, using a subrequest
437  *
438  * @param[in] request The current request.
439  * @Param[in] tls_session The current TLS session.
440  * @return
441  * - UNLANG_ACTION_CALCULATE_RESULT on noop.
442  * - UNLANG_ACTION_PUSHED_CHILD on success.
443  * - UNLANG_ACTION_FAIL on failure.
444  */
445 static unlang_action_t tls_verify_client_cert_push(request_t *request, fr_tls_session_t *tls_session)
446 {
447  fr_tls_conf_t *conf = fr_tls_session_conf(tls_session->ssl);
448  request_t *child;
449  fr_pair_t *vp;
450  unlang_action_t ua;
451 
452  MEM(child = unlang_subrequest_alloc(request, dict_tls));
453  request = child;
454 
455  /*
456  * Add extra pairs to the subrequest
457  */
458  fr_tls_session_extra_pairs_copy_to_child(child, tls_session);
459 
460  /*
461  * Setup the child request for loading
462  * session resumption data.
463  */
465  vp->vp_uint32 = enum_tls_packet_type_verify_certificate->vb_uint32;
466 
468  vp->vp_bool = tls_session->validate.resumed;
469 
470  /*
471  * Allocate a child, and set it up to call
472  * the TLS virtual server.
473  */
474  ua = fr_tls_call_push(child, tls_verify_client_cert_result, conf, tls_session);
475  if (ua < 0) {
476  PERROR("Failed calling TLS virtual server");
477  talloc_free(child);
478  return UNLANG_ACTION_FAIL;
479  }
480 
481  return ua;
482 }
483 
484 /** Clear any previous validation result
485  *
486  * Should be called by the validation requestor to get the result and reset
487  * the validation state.
488  *
489  * @return
490  * - true if the certificate chain was validated.
491  * - false if the certificate chain failed validation.
492  */
493 bool fr_tls_verify_cert_result(fr_tls_session_t *tls_session)
494 {
495  bool result;
496 
497  fr_assert(tls_session->validate.state != FR_TLS_VALIDATION_INIT);
498 
499  result = tls_session->validate.state == FR_TLS_VALIDATION_SUCCESS;
500 
501  tls_session->validate.state = FR_TLS_VALIDATION_INIT;
502  tls_session->validate.resumed = false;
503 
504  return result;
505 }
506 
507 /** Reset the verification state
508  *
509  */
510 void fr_tls_verify_cert_reset(fr_tls_session_t *tls_session)
511 {
512  tls_session->validate.state = FR_TLS_VALIDATION_INIT;
513  tls_session->validate.resumed = false;
514 }
515 
516 /** Setup a verification request
517  *
518  */
519 void fr_tls_verify_cert_request(fr_tls_session_t *tls_session, bool session_resumed)
520 {
521  fr_assert(tls_session->validate.state == FR_TLS_VALIDATION_INIT);
522 
523  tls_session->validate.state = FR_TLS_VALIDATION_REQUESTED;
524  tls_session->validate.resumed = session_resumed;
525 }
526 
527 /** Push a `verify certificate { ... }` section
528  *
529  * @param[in] request The current request.
530  * @Param[in] tls_session The current TLS session.
531  * @return
532  * - UNLANG_ACTION_CALCULATE_RESULT - No pending actions
533  * - UNLANG_ACTION_PUSHED_CHILD - Pending operations to evaluate.
534  */
535 unlang_action_t fr_tls_verify_cert_pending_push(request_t *request, fr_tls_session_t *tls_session)
536 {
537  if (tls_session->validate.state == FR_TLS_VALIDATION_REQUESTED) {
538  return tls_verify_client_cert_push(request, tls_session);
539  }
540 
542 }
543 #endif /* WITH_TLS */
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition: action.h:36
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition: action.h:37
#define store(_store, _var)
Definition: atomic_queue.h:48
#define DIAG_UNKNOWN_PRAGMAS
Definition: build.h:417
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define DIAG_ON(_x)
Definition: build.h:419
#define UNUSED
Definition: build.h:313
#define DIAG_OFF(_x)
Definition: build.h:418
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:137
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition: debug.h:208
static fr_slen_t err
Definition: dict.h:645
bool unlang_request_is_cancelled(request_t const *request)
Return whether a request has been cancelled.
Definition: interpret.c:1310
HIDDEN fr_dict_attr_t const * attr_tls_certificate
Attribute definitions for lib curl.
Definition: base.c:35
void log_request_pair(fr_log_lvl_t lvl, request_t *request, fr_pair_t const *parent, fr_pair_t const *vp, char const *prefix)
Print a fr_pair_t.
Definition: log.c:772
#define PERROR(_fmt,...)
Definition: log.h:228
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition: log.h:443
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition: log.h:335
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RERROR(fmt,...)
Definition: log.h:298
#define RINDENT()
Indent R* messages by one level.
Definition: log.h:430
HIDDEN fr_dict_attr_t const * attr_tls_packet_type
HIDDEN fr_dict_t const * dict_tls
HIDDEN fr_dict_attr_t const * attr_tls_session_resumed
fr_value_box_t const * enum_tls_packet_type_success
fr_value_box_t const * enum_tls_packet_type_verify_certificate
talloc_free(reap)
@ L_DBG_LVL_2
2nd highest priority debug messages (-xx | -X).
Definition: log.h:71
@ L_DBG
Only displayed when debugging is enabled.
Definition: log.h:59
static size_t used
static uint8_t depth(fr_minmax_heap_index_t i)
Definition: minmax_heap.c:83
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:688
unsigned int fr_pair_count_by_da(fr_pair_list_t const *list, fr_dict_attr_t const *da)
Return the number of instances of a given da in the specified list.
Definition: pair.c:665
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_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_delete_by_da(fr_pair_list_t *list, fr_dict_attr_t const *da)
Delete matching pairs from the specified list.
Definition: pair.c:1684
fr_pair_t * fr_pair_find_by_da_idx(fr_pair_list_t const *list, fr_dict_attr_t const *da, unsigned int idx)
Find a pair with a matching da at a given index.
Definition: pair.c:736
static bool done
Definition: radclient.c:80
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG_ENABLED2()
Definition: radclient.h:50
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
static rs_t * conf
Definition: radsniff.c:53
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
#define pair_append_request(_attr, _da)
Allocate and append a fr_pair_t to the request list.
Definition: pair.h:37
#define pair_prepend_request(_attr, _da)
Allocate and prepend a fr_pair_t to the request list.
Definition: pair.h:77
fr_assert(0)
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
request_t * unlang_subrequest_alloc(request_t *parent, fr_dict_t const *namespace)
Allocate a subrequest to run through a virtual server at some point in the future.
Definition: subrequest.c:266
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
Definition: pair_inline.c:125