The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
proto_ldap_sync_ldap.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: 656655e7ae219e82f55aa08f335ee539d7e38800 $
19 * @file proto_ldap_sync_ldap.c
20 * @brief LDAP sync handler.
21 *
22 * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
23 */
25
26#define LOG_PREFIX "proto_ldap_sync_ldap"
27
28#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
29#include <freeradius-devel/internal/internal.h>
30#include <freeradius-devel/server/request.h>
31#include <freeradius-devel/io/listen.h>
32#include <freeradius-devel/unlang/call.h>
33#include <freeradius-devel/util/dbuff.h>
34#include <freeradius-devel/ldap/base.h>
35#include <freeradius-devel/ldap/conf.h>
36
38#include "rfc4533.h"
39#include "persistent_search.h"
40#include "active_directory.h"
41
43
46
48 /*
49 * LDAP server definition
50 */
52
53 /*
54 * Common LDAP conf parsers
55 */
57
58 /*
59 * Network tunable parameters
60 */
61 { FR_CONF_OFFSET_IS_SET("recv_buff", FR_TYPE_UINT32, 0, proto_ldap_sync_ldap_t, recv_buff) },
62 { FR_CONF_OFFSET("max_outstanding", proto_ldap_sync_ldap_t, max_outstanding), .dflt = "65536" },
63
65};
66
69
72 { .out = &dict_ldap_sync, .proto = "ldap" },
73 { .out = &dict_freeradius, .proto = "freeradius" },
75};
76
85
88 { .out = &attr_ldap_sync_packet_id, .name = "Sync-Packet-ID", .type = FR_TYPE_UINT32, .dict = &dict_ldap_sync },
89 { .out = &attr_ldap_sync_cookie, .name = "LDAP-Sync.Cookie", .type = FR_TYPE_OCTETS, .dict = &dict_ldap_sync },
90 { .out = &attr_ldap_sync_entry_dn, .name = "LDAP-Sync.Entry-DN", .type = FR_TYPE_STRING, .dict = &dict_ldap_sync },
91 { .out = &attr_ldap_sync_entry_uuid, .name = "LDAP-Sync.Entry-UUID", .type = FR_TYPE_OCTETS, .dict = &dict_ldap_sync },
92 { .out = &attr_ldap_sync_orig_dn, .name = "LDAP-Sync.Original-DN", .type = FR_TYPE_STRING, .dict = &dict_ldap_sync },
93 { .out = &attr_ldap_sync_root_dn, .name = "LDAP-Sync.Directory-Root-DN", .type = FR_TYPE_STRING, .dict = &dict_ldap_sync },
94 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_ldap_sync },
95 { .out = &attr_ldap_sync_base_dn, .name = "LDAP-Sync-Base-DN", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
97};
98
104
105/** Operations performed on entries
106 */
108 { L("add"), SYNC_OP_ADD },
109 { L("delete"), SYNC_OP_DELETE },
110 { L("modify"), SYNC_OP_MODIFY },
111 { L("present"), SYNC_OP_PRESENT },
112};
114
115/** Context used when looking up Directory types
116 */
123
124/** Context for "load Cookie" retry timed event
125 */
131
132/** Compare two sync state structures on msgid
133 *
134 * @param[in] one first sync to compare.
135 * @param[in] two second sync to compare.
136 * @return CMP(one, two)
137 */
138fr_cmp_ret_t sync_state_cmp(void const *one, void const *two)
139{
140 sync_state_t const *a = one, *b = two;
141
142 return CMP(a->msgid, b->msgid);
143}
144
145/** Tell the remote server to stop the sync
146 *
147 * Terminates the search informing the remote server that we no longer want to receive results
148 * for this sync. A RFC 4511 abandon request is used to inform the server.
149 *
150 * This allows individual syncs to be stopped without destroying the underlying connection.
151 *
152 * Removes the sync's msgid from the tree of msgids associated with the connection.
153 *
154 * @param[in] sync to abandon.
155 * @return 0
156 */
158{
159 fr_ldap_connection_t *conn = talloc_get_type_abort(sync->conn, fr_ldap_connection_t);
160 fr_rb_tree_t *tree = talloc_get_type_abort(conn->uctx, fr_rb_tree_t);
161
162 DEBUG3("Abandoning sync base dn \"%s\", filter \"%s\"", sync->config->base_dn, sync->config->filter);
163
164 trigger(unlang_interpret_get_thread_default(), sync->config->cs, NULL, "modules.ldap_sync.stop", true, &sync->trigger_args, sync);
165
166 if (!sync->conn->handle) return 0; /* Handled already closed? */
167
168 /*
169 * Tell the remote server to stop sending results
170 */
171 if (sync->msgid >= 0) ldap_abandon_ext(sync->conn->handle, sync->msgid, NULL, NULL);
172 fr_rb_delete(tree, &(sync_state_t){.msgid = sync->msgid});
173
174 return 0;
175}
176
177/** Allocate a sync state
178 *
179 * @param[in] ctx to allocate the sync state in.
180 * @param[in] conn which the sync will run on.
181 * @param[in] inst module instance for the sync.
182 * @param[in] sync_no number of the sync in the array of configs.
183 * @param[in] config for the sync.
184 * @return new sync state.
185 */
187 size_t sync_no, sync_config_t const *config)
188{
189 sync_state_t *sync;
190 fr_pair_t *vp;
191
192 MEM(sync = talloc_zero(ctx, sync_state_t));
193 sync->conn = conn;
194 sync->inst = inst;
195 sync->config = config;
196 sync->sync_no = sync_no;
197 sync->phase = SYNC_PHASE_INIT;
198
200
201 /*
202 * Create arguments to pass to triggers
203 */
206 talloc_strlen(config->base_dn), false);
207
208 /*
209 * If the connection is freed, all the sync state is also freed
210 */
211 talloc_set_destructor(sync, sync_state_free);
212
213 return sync;
214}
215
216/** Add a new cookie packet ctx to the pending list
217 *
218 * Does not actually send the packet.
219 *
220 * @param[in] sync the cookie was received for.
221 * @param[in] refresh the sync after storing this cookie.
222 * @return
223 * - 0 on success.
224 * - -1 on failure
225 */
226int ldap_sync_cookie_store(sync_state_t *sync, bool refresh)
227{
228 sync_packet_ctx_t *sync_packet_ctx = NULL;
229 uint8_t *cookie = sync->cookie;
230
231 MEM(sync_packet_ctx = talloc_zero(sync, sync_packet_ctx_t));
232 sync_packet_ctx->sync = sync;
233
234 sync_packet_ctx->type = SYNC_PACKET_TYPE_COOKIE;
235 if (cookie) sync_packet_ctx->cookie = talloc_memdup(sync_packet_ctx, cookie, talloc_array_length(cookie));
236 sync_packet_ctx->refresh = refresh;
237
238 if (fr_dlist_insert_tail(&sync->pending, sync_packet_ctx) < 0) {
239 talloc_free(sync_packet_ctx);
240 return -1;
241 }
242 sync->pending_cookies++;
243
244 return 0;
245}
246
247/** Event to handle storing of cookies on a timed basis
248 *
249 * Looks at the head of the list of pending sync packets for a cookie.
250 * A cookie at the head says that all the previous changes have been
251 * completed, so the cookie can be sent.
252 */
254{
255 sync_state_t *sync = talloc_get_type_abort(uctx, sync_state_t);
256 sync_packet_ctx_t *sync_packet_ctx;
257
258 if (sync->pending_cookies == 0) goto finish;
259
260 /*
261 * Check the head entry in the list - is it a pending cookie
262 */
263 sync_packet_ctx = fr_dlist_head(&sync->pending);
264 if ((sync_packet_ctx->type != SYNC_PACKET_TYPE_COOKIE) ||
265 (sync_packet_ctx->status != SYNC_PACKET_PENDING)) goto finish;
266
267 ldap_sync_cookie_send(sync_packet_ctx);
268
269finish:
270 (void) fr_timer_in(sync, tl, &sync->cookie_ev, sync->inst->cookie_interval,
271 false, ldap_sync_cookie_event, sync);
272}
273
274/** Enqueue a new cookie store packet
275 *
276 * Create a new internal packet containing the cookie we received from the LDAP server.
277 * This allows the administrator to store the cookie and provide it on a future call to
278 * load Cookie.
279 *
280 * @param[in] sync_packet_ctx packet context containing the cookie to store.
281 * @return
282 * - 0 on success.
283 * - -1 on failure.
284*/
286{
287 sync_state_t *sync = sync_packet_ctx->sync;
288 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(sync->config->user_ctx, proto_ldap_sync_ldap_thread_t);
289 fr_dbuff_t *dbuff;
290 fr_pair_list_t pairs;
291 fr_pair_t *vp;
292 TALLOC_CTX *local = NULL;
293 uint8_t *cookie = sync_packet_ctx->cookie;
294
295 if (sync_packet_ctx->status != SYNC_PACKET_PENDING) return 0;
296 sync_packet_ctx->status = SYNC_PACKET_PREPARING;
297
298 FR_DBUFF_TALLOC_THREAD_LOCAL(&dbuff, 1024, 4096);
299
300 local = talloc_new(NULL);
301 fr_pair_list_init(&pairs);
302 if (fr_pair_list_copy(local, &pairs, &sync->config->sync_pairs) < 0) {
303 error:
304 talloc_free(local);
305 return -1;
306 }
307
309 if (!vp) goto error;
310
312 if (!vp) goto error;
313
314 /*
315 * Add the cookie to the packet, if set.
316 * If the server has indicated a refresh is required it can do so
317 * with no cookie set - so we store a blank cookie to clear anything
318 * which was previously stored.
319 */
320 if (cookie) {
322 cookie, talloc_array_length(cookie), true);
323 if (!vp) goto error;
324 }
325
326 if (fr_internal_encode_list(dbuff, &pairs, &encode_ctx) < 0) goto error;
327 talloc_free(local);
328
329 if (fr_network_listen_send_packet(thread->nr, thread->li, thread->li, fr_dbuff_buff(dbuff),
330 fr_dbuff_used(dbuff), fr_time(), sync_packet_ctx) < 0) {
331 sync_packet_ctx->status = SYNC_PACKET_PENDING;
332 return -1;
333 }
334
335 sync_packet_ctx->status = SYNC_PACKET_PROCESSING;
336
337 return 0;
338}
339
340/** Send a change packet to the workers
341 *
342 * Called each time a change packet is received and also from a
343 * timer event retrying packets which previously failed to send.
344 *
345 * @param sync_packet_ctx Packet to send
346 * @return
347 * - 0 on success.
348 * - -1 on failure.
349 */
351{
352 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(sync_packet_ctx->sync->config->user_ctx,
354 fr_dbuff_t *dbuff;
355
356 FR_DBUFF_TALLOC_THREAD_LOCAL(&dbuff, 1024, 4096);
357
358 if (fr_internal_encode_list(dbuff, &sync_packet_ctx->pairs, &encode_ctx) < 0) return -1;
359 if (fr_network_listen_send_packet(thread->nr, thread->li, thread->li, fr_dbuff_buff(dbuff),
360 fr_dbuff_used(dbuff), fr_time(), sync_packet_ctx) < 0) return -1;
361
362 sync_packet_ctx->status = SYNC_PACKET_PROCESSING;
363 fr_pair_list_free(&sync_packet_ctx->pairs);
364
365 return 0;
366}
367
368/** Event to handle sending of any change packets which failed to send.
369 *
370 * Looks at the head of the list of pending sync packets for unsent
371 * change packets and sends any up to the first cookie.
372 */
373static void ldap_sync_retry_event(fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
374{
375 sync_state_t *sync = talloc_get_type_abort(uctx, sync_state_t);
376 sync_packet_ctx_t *sync_packet_ctx = NULL;
377
378 while ((sync_packet_ctx = fr_dlist_next(&sync->pending, sync_packet_ctx))) {
379 if (sync_packet_ctx->type != SYNC_PACKET_TYPE_CHANGE) break;
380 if (sync_packet_ctx->status != SYNC_PACKET_PENDING) continue;
381
382 /*
383 * Retry sending packet. Don't try any more if it fails.
384 */
385 if (ldap_sync_entry_send_network(sync_packet_ctx) < 0) break;
386 }
387
388 /*
389 * We didn't run through the whole list, so there may be other pending
390 * packets - reschedule a retry event.
391 */
392 if (sync_packet_ctx) {
393 (void) fr_timer_in(sync, tl, &sync->retry_ev, sync->inst->retry_interval,
394 false, ldap_sync_retry_event, sync);
395 }
396}
397
404
405/** Enqueue a new entry change packet.
406 *
407 * @param[in] sync notification has arrived for.
408 * @param[in] uuid of the entry (RFC 4533 only).
409 * @param[in] orig_dn original DN of the entry - provided by those directories
410 * implementing persistent search, when an entry is renamed.
411 * @param[in] msg containing the entry.
412 * @param[in] op The type of modification we need to perform to our
413 * representation of the entry.
414 * @return
415 * - 0 on success.
416 * - -1 on failure.
417 */
418int ldap_sync_entry_send(sync_state_t *sync, uint8_t const uuid[SYNC_UUID_LENGTH], struct berval *orig_dn,
419 LDAPMessage *msg, sync_op_t op)
420{
422 fr_pair_list_t *pairs;
423 fr_pair_t *vp;
424 sync_packet_ctx_t *sync_packet_ctx = NULL;
425
426 MEM(sync_packet_ctx = talloc_zero(sync, sync_packet_ctx_t));
427 sync_packet_ctx->sync = sync;
428
429 fr_pair_list_init(&sync_packet_ctx->pairs);
430 pairs = &sync_packet_ctx->pairs;
431
432 if (fr_pair_list_copy(sync_packet_ctx, pairs, &sync->config->sync_pairs) < 0) {
433 error:
434 if (msg) ldap_msgfree(msg);
435 talloc_free(sync_packet_ctx);
436 return -1;
437 }
438
439 pcode = sync_packet_code_table[op];
440
441 fr_pair_list_append_by_da(sync_packet_ctx, vp, pairs, attr_packet_type, (uint32_t)pcode, false);
442 if (!vp) goto error;
443
444 fr_pair_list_append_by_da(sync_packet_ctx, vp, pairs, attr_ldap_sync_packet_id, (uint32_t)sync->sync_no, false);
445 if (!vp) goto error;
446
447 /*
448 * Add the UUID if provided
449 */
450 if (uuid) {
452 uuid, SYNC_UUID_LENGTH, true);
453 if (!vp) goto error;
454 }
455
456 /*
457 * Add the original DN if provided
458 */
459 if (orig_dn && (orig_dn->bv_len > 0)) {
461 orig_dn->bv_val, orig_dn->bv_len, true);
462 if (!vp) goto error;
463 }
464
465 /*
466 * Add the entry DN if there is an LDAP message to read
467 */
468 if (msg) {
469 char *entry_dn = ldap_get_dn(sync->conn->handle, msg);
470 map_t const *map = NULL;
471 struct berval **values;
472 int count, i;
473
475 entry_dn, strlen(entry_dn), true);
476 if (!vp) goto error;
477
478 ldap_memfree(entry_dn);
479
480 /*
481 * Map LDAP returned attributes to pairs as per update map
482 */
483 while ((map = map_list_next(&sync->config->entry_map, map))) {
484 values = ldap_get_values_len(fr_ldap_handle_thread_local(), msg, map->rhs->name);
485 if (!values) goto next;
486
487 count = ldap_count_values_len(values);
488
489 for (i = 0; i < count; i++) {
490 if (values[i]->bv_len == 0) continue;
491
492 if (pair_append_by_tmpl_parent(sync_packet_ctx, &vp, pairs, map->lhs, true) < 0) break;
493 if (fr_value_box_from_str(vp, &vp->data, vp->vp_type, NULL, values[i]->bv_val,
494 values[i]->bv_len, NULL) < 0) {
495 fr_pair_remove(pairs, vp);
497 }
498
499 /* Only += operator adds multiple values */
500 if (map->op != T_OP_ADD_EQ) break;
501 }
502 next:
503 ldap_value_free_len(values);
504 }
505 }
506
507 if (fr_dlist_insert_tail(&sync->pending, sync_packet_ctx) < 0) goto error;
508
509 ldap_msgfree(msg);
510
511 /*
512 * Send the packet and if it fails to send add a retry event
513 */
514 if ((ldap_sync_entry_send_network(sync_packet_ctx) < 0) &&
515 (fr_timer_in(sync, sync->conn->conn->el->tl, &sync->retry_ev,
516 sync->inst->retry_interval, false, ldap_sync_retry_event, sync) < 0)) {
517 PERROR("Inserting LDAP sync retry timer failed");
518 }
519
520 return 0;
521}
522
524 UNUSED connection_state_t state, void *uctx);
525
527 UNUSED connection_state_t state, void *uctx);
528
529/** Attempt to (re)initialise a connection
530 *
531 * Performs complete re-initialization of a connection. Called during socket_open
532 * to create the initial connection and again any time we need to reopen the connection.
533 *
534 * @param[in] tl the event list managing listen event.
535 * @param[in] now current time.
536 * @param[in] user_ctx Listener.
537 */
538static void proto_ldap_connection_init(fr_timer_list_t *tl, UNUSED fr_time_t now, void *user_ctx)
539{
540 fr_listen_t *listen = talloc_get_type_abort(user_ctx, fr_listen_t);
541 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(listen->thread_instance, proto_ldap_sync_ldap_thread_t);
543
544 if (thread->conn) talloc_free(thread->conn);
545
546 /*
547 * Allocate an outbound LDAP connection
548 */
549 thread->conn = connection_alloc(thread, thread->el,
551 .init = fr_ldap_connection_init,
552 .close = fr_ldap_connection_close
553 },
555 .connection_timeout = inst->handle_config.net_timeout,
556 .reconnection_delay = inst->handle_config.reconnection_delay
557 },
558 "ldap_sync", &inst->handle_config);
559
560 if (!thread->conn) {
561 PERROR("Failed (re)initialising connection, will retry in %pV seconds",
562 fr_box_time_delta(inst->handle_config.reconnection_delay));
563
564 if (fr_timer_in(thread, tl, &thread->conn_retry_ev,
565 inst->handle_config.reconnection_delay,
566 false, proto_ldap_connection_init, listen) < 0) {
567 FATAL("Failed inserting event: %s", fr_strerror());
568 }
569 }
570
571 /*
572 * Add watch functions on the LDAP connection
573 */
575 _proto_ldap_socket_init, true, thread);
576
579
580 /*
581 * Signal the connection to start
582 */
584
585 return;
586}
587
588/** Child listener mod_close
589 *
590 * Ensures the LDAP connection is signalled to close gracefully when
591 * the listener is closed.
592 */
594{
596
598 return 0;
599}
600
601/** LDAP sync mod_read for child listener
602 *
603 * Called when there is data to read on the LDAP connection
604 *
605 * Actual packets are created by the various callbacks since a single LDAP
606 * message can result in multiple packets to process e.g.:
607 *
608 * - Sync Info Message with syncInfoValue of syncIdSet can reference
609 * multiple directory entries.
610 * - Various sync related messages can include a new cookie in
611 * addition to their other data.
612 */
614 UNUSED size_t buffer_len, UNUSED size_t *leftover)
615{
617 fr_ldap_connection_t *conn = talloc_get_type_abort(thread->conn->h, fr_ldap_connection_t);
618 struct timeval poll = { 1, 0 };
619 LDAPMessage *msg = NULL;
620 int ret = 0;
621 fr_ldap_rcode_t rcode;
622 sync_state_t *sync = NULL;
623 fr_rb_tree_t *tree;
624 int type, msgid;
625 LDAPControl **ctrls = NULL;
626 sync_msg_t callback = NULL;
627
628 fr_assert(conn);
629
630 /*
631 * If there are already too many outstanding requests just return.
632 * This will (potentially) cause the TCP buffer to fill and push the
633 * backpressure back to the LDAP server.
634 */
635 if (fr_network_listen_outstanding(thread->nr, li) >= thread->inst->max_outstanding) return 0;
636
637 tree = talloc_get_type_abort(conn->uctx, fr_rb_tree_t);
638
639 /*
640 * Pull the next outstanding message from this connection.
641 * We process one message at a time so that the message can be
642 * passed to the worker, and freed once the request has been
643 * handled.
644 */
645 ret = ldap_result(conn->handle, LDAP_RES_ANY, LDAP_MSG_ONE, &poll, &msg);
646
647 switch (ret) {
648 case 0: /*
649 * Timeout - this has been observed if changes are being
650 * processed slowly, the TCP receive buffer fills and
651 * the LDAP directory pauses sending data for a period.
652 * Then all pending changes are processed and the receive buffer
653 * is emptied.
654 * The situation resolves when the directory starts sending
655 * data again.
656 */
657 return 0;
658
659 case -1:
660 rcode = fr_ldap_error_check(NULL, conn, NULL, NULL);
661 if (rcode == LDAP_PROC_BAD_CONN) return -2;
662 return -1;
663
664 default:
665 break;
666 }
667
668 /*
669 * De-multiplex based on msgid
670 */
671 if (!msg) return 0;
672
673 msgid = ldap_msgid(msg);
674 type = ldap_msgtype(msg);
675
676 ret = 0;
677 if (msgid == 0) {
678 WARN("Ignoring unsolicited %s message",
680 free_msg:
681 if (ctrls) ldap_controls_free(ctrls);
682 ldap_msgfree(msg);
683 return ret;
684 }
685
686 fr_rb_find((void **)&sync, tree, &(sync_state_t){.msgid = msgid});
687 if (!sync) {
688 WARN("Ignoring msgid %i, doesn't match any outstanding syncs", msgid);
689 goto free_msg;
690 }
691
692 /*
693 * Check for errors contained within the message.
694 * This has to be per message, as multiple syncs
695 * are multiplexed together on one connection.
696 */
697 switch (fr_ldap_error_check(&ctrls, conn, msg, sync->config->base_dn)) {
699 break;
700
701 /*
702 * The e-syncRefresRequired result code is the server informing us that
703 * the query needs to be restarted for a new refresh phase to run.
704 * It is sent as the result code for a SearchResultsDone message.
705 */
707 if (type != LDAP_RES_SEARCH_RESULT) {
708 PERROR("e-syncRefreshRequired result code received on wrong message type");
709 ret = -1;
710 goto free_msg;
711 }
712
713 DEBUG2("LDAP Server returned e-syncRefreshRequired");
714 if (sync->config->refresh) {
715 return sync->config->refresh(sync, msg, ctrls);
716 }
717 goto free_msg;
718
719 /*
720 * Don't think this should happen... but libldap
721 * is wonky sometimes...
722 */
724 PERROR("Connection unusable");
725 ret = -2;
726 goto free_msg;
727
728 default:
729 PERROR("Sync error");
730 ret = -1;
731 goto free_msg;
732 }
733
734 DEBUG3("Got %s message for sync (msgid %i)",
736
737 switch (type) {
738 case LDAP_RES_SEARCH_REFERENCE:
739 case LDAP_RES_SEARCH_ENTRY:
740 callback = sync->config->entry;
741 break;
742
743 case LDAP_RES_INTERMEDIATE:
744 callback = sync->config->intermediate;
745 break;
746
747 default:
748 WARN("Ignoring unexpected message type (%i)", type);
749 ret = 0;
750 goto free_msg;
751 }
752
753 if (callback) {
754 ret = callback(sync, msg, ctrls);
755 if (ret < 0) PERROR("Sync callback error");
756 } else {
757 /*
758 * Callbacks are responsible for freeing the msg
759 * so if there is no callback, free it.
760 */
761 ldap_msgfree(msg);
762 }
763
764 ldap_controls_free(ctrls);
765
766 return ret;
767}
768
769/** Send a fake packet to run the "load Cookie" section
770 *
771 * @param ctx Context to allocate temporary pairs in.
772 * @param inst LDAP sync configuration.
773 * @param sync_no Id of the sync whose.
774 * @param thread Thread specific LDAP sync data.
775 * @return
776 * - 0 on success
777 * - -1 on failure
778 */
779static int proto_ldap_cookie_load_send(TALLOC_CTX *ctx, proto_ldap_sync_ldap_t const *inst, size_t sync_no,
781 size_t len;
782 sync_config_t *config = inst->parent->sync_config[sync_no];
783 fr_pair_list_t pairs;
784 fr_pair_t *vp;
785 fr_dbuff_t *dbuff;
786 fr_ldap_connection_t *ldap_conn = thread->conn->h;
787
788 fr_pair_list_init(&pairs);
789 if (unlikely(fr_pair_list_copy(ctx, &pairs, &config->sync_pairs) < 0)) return -1;
790
791 /*
792 * Ensure we have access to the thread instance
793 * in for the demux callbacks
794 */
795 inst->parent->sync_config[sync_no]->user_ctx = thread;
796
797 /*
798 * Assess the namingContext which applies to this sync
799 */
800 if (ldap_conn->directory->naming_contexts) {
801 char const * const *contexts = ldap_conn->directory->naming_contexts;
802 size_t j, num = talloc_str_array_len(contexts);
803 size_t base_dn_len = talloc_strlen(config->base_dn);
804
805 for (j = 0; j < num; j++) {
806 len = talloc_strlen(contexts[j]);
807 if (base_dn_len < len) continue;
808
809 if (strncasecmp(&config->base_dn[base_dn_len - len], contexts[j], len) == 0) {
810 config->root_dn = contexts[j];
811 break;
812 }
813 }
814 }
815
816 /*
817 * Set up callbacks based on directory type.
818 */
819 switch (ldap_conn->directory->sync_type) {
823 config->intermediate = rfc4533_sync_intermediate;
825 break;
826
830 break;
831
835 break;
836
837 default:
838 fr_assert(0);
839 }
840
843 if (!vp) return -1;
844 fr_pair_list_append_by_da(ctx, vp, &pairs, attr_ldap_sync_packet_id, (uint32_t)sync_no, false);
845 if (!vp) return -1;
846
847 if (config->root_dn) {
849 config->root_dn, strlen(config->root_dn), false);
850 if (!vp) return -1;
851 }
852
853 FR_DBUFF_TALLOC_THREAD_LOCAL(&dbuff, 1024, 4096);
854
855 if (fr_internal_encode_list(dbuff, &pairs, &encode_ctx) < 0) return -1;
856
857 if (fr_network_listen_send_packet(thread->nr, thread->li, thread->li,
858 fr_dbuff_buff(dbuff), fr_dbuff_used(dbuff),
859 fr_time(), NULL) < 0) return -1;
860 fr_pair_list_free(&pairs);
861 return 0;
862}
863
864/** Timer event to retry running "load Cookie" on failures
865 *
866 */
868{
869 proto_ldap_cookie_load_retry_ctx *retry_ctx = talloc_get_type_abort(uctx, proto_ldap_cookie_load_retry_ctx);
870
871 DEBUG2("Retrying \"load Cookie\" for sync no %ld", retry_ctx->sync_no);
872 if (proto_ldap_cookie_load_send(retry_ctx, retry_ctx->inst, retry_ctx->sync_no,
873 retry_ctx->thread) < 0) {
874 ERROR("Failed retrying \"load Cookie\". Will try again in %pV seconds",
876 (void) fr_timer_in(retry_ctx->thread->conn->h, tl,
877 &retry_ctx->inst->parent->sync_config[retry_ctx->sync_no]->ev,
879 false, proto_ldap_cookie_load_retry, retry_ctx);
880 return;
881 }
882 talloc_free(retry_ctx);
883}
884
885/** LDAP sync mod_write for child listener
886 *
887 * Handle any returned data after the worker has processed the packet and,
888 * for packets where tracking structures were used, ensure they are freed.
889 */
890static ssize_t proto_ldap_child_mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t request_time,
891 uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
892{
895 fr_dbuff_t dbuff;
897 uint32_t packet_id;
898 fr_pair_list_t tmp;
899 fr_pair_t *vp = NULL;
900 ssize_t ret;
901 TALLOC_CTX *local;
902 sync_packet_ctx_t *sync_packet_ctx = NULL;
903
904 local = talloc_new(NULL);
905 fr_dbuff_init(&dbuff, buffer, buffer_len);
906
907 if (packet_ctx) sync_packet_ctx = talloc_get_type_abort(packet_ctx, sync_packet_ctx_t);
908
909 /*
910 * Extract returned attributes into a temporary list
911 */
912 fr_pair_list_init(&tmp);
913
914 ret = fr_internal_decode_list_dbuff(local, &tmp, fr_dict_root(dict_ldap_sync), &dbuff, NULL);
915 if (ret < 0) goto finish;
916
917 /*
918 * There should always be a packet ID and code
919 */
921 fr_assert(vp);
922 packet_id = vp->vp_uint32;
923
925 fr_assert(vp);
926 pcode = vp->vp_uint32;
927
928 switch (pcode) {
930 {
931 uint8_t *cookie = NULL;
932
933 /*
934 * If the received packet ID is greater than the number of syncs
935 * we have then something very bad has happened
936 */
937 fr_assert (packet_id < talloc_array_length(inst->parent->sync_config));
938
939 /*
940 * Look for the returned cookie.
941 */
943 if (vp) cookie = talloc_memdup(inst, vp->vp_octets, vp->vp_length);
944
945 if (inst->parent->sync_config[packet_id]->init(thread->conn->h, packet_id, inst->parent, cookie) < 0) {
946 ret = -1;
947 goto finish;
948 }
949 }
950 break;
951
953 break;
954
956 {
957 sync_config_t const *sync_config;
958
959 if (!sync_packet_ctx || !sync_packet_ctx->refresh) break;
960
961 /*
962 * Abandon the old sync and start a new one with the relevant cookie.
963 */
964 sync_config = sync_packet_ctx->sync->config;
965 DEBUG3("Restarting sync with base %s", sync_config->base_dn);
966 talloc_free(sync_packet_ctx->sync);
967 if (inst->parent->sync_config[packet_id]->init(thread->conn->h, packet_id, inst->parent,
968 sync_packet_ctx->cookie) < 0) {
969 ret = -1;
970 goto finish;
971 }
972 }
973 break;
974
976 {
978
979 ERROR("Load Cookie failed for sync %d, retrying in %pV seconds", packet_id,
980 fr_box_time_delta(inst->handle_config.reconnection_delay));
981
982 MEM(retry_ctx = talloc(thread, proto_ldap_cookie_load_retry_ctx));
984 .thread = thread,
985 .inst = inst,
986 .sync_no = packet_id,
987 };
988
989 (void) fr_timer_in(thread->conn->h, thread->el->tl, &inst->parent->sync_config[packet_id]->ev,
990 inst->handle_config.reconnection_delay,
991 false, proto_ldap_cookie_load_retry, retry_ctx);
992 }
993 break;
994
995 default:
996 ERROR("Invalid packet type returned %d", pcode);
997 break;
998 }
999
1000 if (sync_packet_ctx) {
1001 sync_state_t *sync = sync_packet_ctx->sync;
1003 proto_ldap_sync_t *ldap_sync = inst->parent;
1004
1005 sync_packet_ctx->status = SYNC_PACKET_COMPLETE;
1006
1007 /*
1008 * A cookie has been stored, reset the counter of changes
1009 */
1010 if (sync_packet_ctx->type == SYNC_PACKET_TYPE_COOKIE) sync->changes_since_cookie = 0;
1011
1012 /*
1013 * Pop any processed updates from the head of the list
1014 */
1015 while ((pc = fr_dlist_head(&sync->pending))) {
1016 /*
1017 * If the head entry in the list is a pending cookie but we have
1018 * not processed enough entries and there are more pending
1019 * cookies, mark this one as processed.
1020 */
1021 if ((pc->type == SYNC_PACKET_TYPE_COOKIE) && (pc->status == SYNC_PACKET_PENDING) &&
1022 (sync->changes_since_cookie < ldap_sync->cookie_changes) &&
1023 (sync->pending_cookies > 1)) pc->status = SYNC_PACKET_COMPLETE;
1024
1025 if (pc->status != SYNC_PACKET_COMPLETE) break;
1026
1027 /*
1028 * Update counters depending on entry type
1029 */
1030 if (pc->type == SYNC_PACKET_TYPE_COOKIE) {
1031 sync->pending_cookies--;
1032 } else {
1033 sync->changes_since_cookie++;
1034 }
1035 pc = fr_dlist_pop_head(&sync->pending);
1036 talloc_free(pc);
1037 }
1038
1039 /*
1040 * If the head of the list is a cookie which has not yet
1041 * been processed and sufficient changes have been recorded
1042 * send the cookie.
1043 */
1044 if (pc && (pc->type == SYNC_PACKET_TYPE_COOKIE) && (pc->status == SYNC_PACKET_PENDING) &&
1046 }
1047
1048finish:
1049 fr_pair_list_free(&tmp);
1050 talloc_free(local);
1051
1052 return ret;
1053}
1054
1055/** Callback for socket errors when running initial root query
1056 */
1058 UNUSED int fd_errno, void *uctx)
1059{
1060 proto_ldap_dir_ctx *dir_ctx = talloc_get_type_abort(uctx, proto_ldap_dir_ctx);
1061 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(dir_ctx->conn->h, fr_ldap_connection_t);
1062
1063 talloc_free(dir_ctx);
1064 fr_ldap_state_error(ldap_conn);
1065}
1066
1067/** Callback to process results of initial root query, identifying directory type
1068 */
1069static void _proto_ldap_socket_open_read(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
1070{
1071 proto_ldap_dir_ctx *dir_ctx = talloc_get_type_abort(uctx, proto_ldap_dir_ctx);
1072 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(dir_ctx->conn->h, fr_ldap_connection_t);
1075 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(dir_ctx->main_listen->thread_instance,
1077 fr_ldap_rcode_t status;
1078 LDAPMessage *result;
1079
1080 size_t i;
1081 TALLOC_CTX *local = NULL;
1082
1083 /*
1084 * Fetch the result. Setting the timeout to 0 here means use
1085 * res_timeout from the configuration.
1086 */
1087 status = fr_ldap_result(&result, NULL, ldap_conn, dir_ctx->msgid, LDAP_MSG_ALL, NULL, fr_time_delta_from_msec(0));
1088 if (status != LDAP_PROC_SUCCESS) {
1089 PERROR("Failed querying for directory type");
1090 if (result) ldap_msgfree(result);
1091 error:
1092 talloc_free(dir_ctx);
1093 if (local) talloc_free(local);
1095 return;
1096 }
1097
1098 fr_ldap_directory_result_parse(ldap_conn->directory, ldap_conn->handle, result, ldap_conn->config->name);
1099 ldap_msgfree(result);
1100
1101 /*
1102 * If the server does not support any of the relevant controls, we just
1103 * tidy up - no point in signalling to reconnect.
1104 */
1105 if (ldap_conn->directory->sync_type == FR_LDAP_SYNC_NONE) {
1106 ERROR("LDAP sync configured for directory which does not support any suitable control");
1107 talloc_free(dir_ctx);
1108 connection_signal_halt(ldap_conn->conn);
1109 return;
1110 }
1111
1112 /*
1113 * We've done all the preparation work on the LDAP connection, now
1114 * use normal network event listeners.
1115 */
1117 if (unlikely(fr_network_listen_add(thread->nr, thread->li) < 0)) {
1118 PERROR("Failed adding listener");
1119 goto error; /* retry? */
1120 }
1121
1122 DEBUG2("Starting sync(s)");
1123
1124 local = talloc_new(NULL);
1125
1126 /*
1127 * Sync operations start by sending a fake packet to run
1128 * the load Cookie section in order to retrieve the cookie
1129 */
1130 for (i = 0; i < talloc_array_length(inst->parent->sync_config); i++) {
1131 if (proto_ldap_cookie_load_send(local, inst, i, thread) < 0) goto error;
1132 }
1133
1134 talloc_free(dir_ctx);
1135 talloc_free(local);
1136}
1137
1138/** Allocate a child listener
1139 *
1140 * Called as a watch function when the LDAP connection enters the INIT state
1141 */
1143 UNUSED connection_state_t state, void *uctx)
1144{
1145 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(uctx, proto_ldap_sync_ldap_thread_t);
1146 fr_listen_t *li;
1147
1148 MEM(li = talloc_zero(conn, fr_listen_t));
1149
1150 thread->li = li;
1151 li->thread_instance = thread;
1152
1153 li->cs = thread->parent->cs;
1155 li->name = li->app_io->common.name;
1157
1158 /*
1159 * Use the app from the parent listener to access
1160 * the encoder / decoder functions
1161 */
1162 li->app = thread->parent->app;
1163 li->app_instance = thread->parent->app_instance;
1164 li->server_cs = thread->inst->parent->server_cs;
1165}
1166
1167/** Callback for closure of LDAP connection
1168 *
1169 * Schedules re-start of the connection if appropriate
1170 */
1172 UNUSED connection_state_t state, void *uctx)
1173{
1174 fr_listen_t *listen = talloc_get_type_abort(uctx, fr_listen_t);
1175 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(listen->thread_instance, proto_ldap_sync_ldap_thread_t);
1176 proto_ldap_sync_ldap_t const *inst = thread->inst;
1177
1178 if (fr_event_loop_exiting(thread->el)) return;
1179
1180 if (prev == CONNECTION_STATE_CONNECTED) {
1181 ERROR("LDAP connection closed. Scheduling restart in %pVs",
1182 fr_box_time_delta(inst->handle_config.reconnection_delay));
1183 if (fr_timer_in(thread, thread->el->tl, &thread->conn_retry_ev,
1184 inst->handle_config.reconnection_delay,
1185 false, proto_ldap_connection_init, listen) < 0) {
1186 FATAL("Failed inserting event: %s", fr_strerror());
1187 }
1188 }
1189}
1190
1191/** Query an LDAP server to establish its type
1192 *
1193 * Called as a watch function once the LDAP connection enters the CONNECTED state
1194 *
1195 * There are three different forms of LDAP sync/persistent search - so we need
1196 * to know what we're dealing with, and whether the relevant options have been enabled.
1197 */
1199 UNUSED connection_state_t state, void *uctx)
1200{
1201 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(uctx, proto_ldap_sync_ldap_thread_t);
1202 fr_listen_t *listen = talloc_get_type_abort(thread->parent, fr_listen_t);
1205 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
1206
1207 proto_ldap_dir_ctx *dir_ctx;
1208
1209 if (ldap_conn->fd < 0) {
1210 connection_failed:
1211 if (fr_timer_in(thread, thread->el->tl, &thread->conn_retry_ev,
1212 inst->handle_config.reconnection_delay,
1213 false, proto_ldap_connection_init, listen) < 0) {
1214 FATAL("Failed inserting event: %s", fr_strerror());
1215 }
1216 return;
1217 }
1218
1219 thread->li->fd = ldap_conn->fd;
1220
1221 MEM(dir_ctx = talloc_zero(inst, proto_ldap_dir_ctx));
1222 if (!dir_ctx) goto connection_failed;
1223
1224 dir_ctx->main_listen = listen;
1225 dir_ctx->conn = conn;
1226 dir_ctx->child_listen = thread->li;
1227
1228#ifdef SO_RCVBUF
1229 if (inst->recv_buff_is_set) {
1230 int opt;
1231
1232 opt = inst->recv_buff;
1233 if (setsockopt(ldap_conn->fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(int)) < 0) {
1234 WARN("Failed setting 'recv_buff': %s", fr_syserror(errno));
1235 }
1236 }
1237#endif
1238
1239 /*
1240 * Set the callback which will handle the results of this query
1241 */
1242 if (fr_event_fd_insert(conn, NULL, conn->el, ldap_conn->fd,
1244 NULL,
1246 dir_ctx) < 0) {
1247 goto connection_failed;
1248 }
1249
1250 /*
1251 * Allocate the directory structure and send the query
1252 */
1253 dir_ctx->msgid = fr_ldap_conn_directory_alloc_async(ldap_conn);
1254 if (dir_ctx->msgid < 0) {
1255 talloc_free(dir_ctx);
1256 goto connection_failed;
1257 }
1258
1259 /*
1260 * Add a watch to catch closed LDAP connections
1261 */
1263 _proto_ldap_socket_closed, true, listen);
1264}
1265
1266/** Callback triggered when parent listener app_io has its event list set
1267 *
1268 * Initiates the actual outbound LDAP connection
1269 *
1270 * @param[in] li The parent listener.
1271 * @param[in] el Event list for this listener.
1272 * @param[in] nr Network handler.
1273 */
1275{
1278
1279 /*
1280 * Set up thread data
1281 */
1282 thread->name = inst->handle_config.name;
1283 thread->parent = li;
1284 thread->el = el;
1285 thread->nr = nr;
1286 thread->inst = inst;
1287
1288 /*
1289 * Initialise the connection
1290 */
1292}
1293
1294static int mod_instantiate(module_inst_ctx_t const *mctx)
1295{
1296 proto_ldap_sync_ldap_t *inst = talloc_get_type_abort(mctx->mi->data, proto_ldap_sync_ldap_t);
1297 CONF_SECTION *conf = mctx->mi->conf;
1298 char const *server;
1299
1300 /*
1301 * Verify that the LDAP server configuration is valid, either
1302 * distinct server and port or an LDAP url.
1303 */
1304 fr_assert(inst->server);
1305
1306 inst->parent = talloc_get_type_abort(mctx->mi->parent->data, proto_ldap_sync_t);
1307 inst->cs = conf;
1308
1309 if (inst->recv_buff_is_set) {
1310 FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, >=, 32);
1311 FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, <=, INT_MAX);
1312 }
1313
1314 server = inst->server;
1315 inst->handle_config.server = talloc_strdup(inst, "");
1316
1317 if (ldap_is_ldap_url(server)) {
1318 if (fr_ldap_server_url_check(&inst->handle_config, server, conf) < 0) return -1;
1319 } else {
1320 if (fr_ldap_server_config_check(&inst->handle_config, server, conf) < 0) return -1;
1321 }
1322
1323 inst->handle_config.server[talloc_strlen(inst->handle_config.server)] = '\0';
1324
1325 inst->handle_config.name = talloc_typed_asprintf(inst, "proto_ldap_conn (%s)",
1327
1328 return 0;
1329}
1330
1332 .common = {
1333 .magic = MODULE_MAGIC_INIT,
1334 .name = "ldap_sync_child"
1335 },
1339
1340 .default_message_size = 4096,
1341 .track_duplicates = false,
1342};
1343
1345 .common = {
1346 .magic = MODULE_MAGIC_INIT,
1347 .name = "ldap_sync_ldap",
1349 .inst_size = sizeof(proto_ldap_sync_ldap_t),
1350 .thread_inst_size = sizeof(proto_ldap_sync_ldap_thread_t),
1351 .instantiate = mod_instantiate
1352 },
1353
1354 .default_message_size = 4096,
1355 .track_duplicates = false,
1356
1357 .event_list_set = mod_event_list_set,
1358};
int active_directory_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, proto_ldap_sync_t const *inst, UNUSED uint8_t const *cookie)
Allocate a sync state structure and issue the search.
int active_directory_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, UNUSED LDAPControl **ctrls)
Handle a LDAP_RES_SEARCH_ENTRY (SearchResultEntry) response.
static int const char char buffer[256]
Definition acutest.h:576
log_entry msg
Definition acutest.h:794
module_t common
Common fields to all loadable modules.
Definition app_io.h:34
size_t default_message_size
Usually maximum message size.
Definition app_io.h:39
Public structure describing an I/O path for a protocol.
Definition app_io.h:33
#define USES_APPLE_DEPRECATED_API
Definition build.h:547
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:113
#define unlikely(_x)
Definition build.h:455
#define UNUSED
Definition build.h:384
#define NUM_ELEMENTS(_t)
Definition build.h:406
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:669
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:529
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:280
#define FR_CONF_OFFSET_IS_SET(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct,...
Definition cf_parse.h:294
#define FR_CONF_OFFSET_FLAGS(_name, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:268
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition cf_parse.h:429
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:606
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:695
char const * cf_section_name(CONF_SECTION const *cs)
Return name2 if set, else name1.
Definition cf_util.c:1374
#define cf_parent(_cf)
Definition cf_util.h:118
static int max_outstanding
connection_state_t
Definition connection.h:47
@ CONNECTION_STATE_CLOSED
Connection has been closed.
Definition connection.h:57
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition connection.h:54
@ CONNECTION_STATE_INIT
Init state, sets up connection.
Definition connection.h:51
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition connection.h:89
Holds a complete set of functions for a connection.
Definition connection.h:199
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
Definition dbuff.h:775
#define fr_dbuff_init(_out, _start, _len_or_end)
Initialise an dbuff for encoding or decoding.
Definition dbuff.h:362
#define fr_dbuff_buff(_dbuff_or_marker)
Return the underlying buffer in a dbuff or one of marker.
Definition dbuff.h:890
#define FR_DBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
Create a function local and thread local extensible dbuff.
Definition dbuff.h:564
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:40
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2637
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:468
static void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition dlist.h:654
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:257
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:537
#define fr_event_fd_insert(...)
Definition event.h:247
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:83
#define GLOBAL_LIB_TERMINATOR
Definition global_lib.h:51
Structure to define how to initialise libraries with global configuration.
Definition global_lib.h:38
talloc_free(hp)
bool allow_name_only
Allow name only pairs.
Definition internal.h:36
unlang_interpret_t * unlang_interpret_get_thread_default(void)
Get the default interpreter for this thread.
Definition interpret.c:2516
int fr_network_listen_send_packet(fr_network_t *nr, fr_listen_t *parent, fr_listen_t *li, const uint8_t *buffer, size_t buflen, fr_time_t recv_time, void *packet_ctx)
Send a packet to the worker.
Definition network.c:815
int fr_network_listen_add(fr_network_t *nr, fr_listen_t *li)
Add a fr_listen_t to a network.
Definition network.c:235
ssize_t fr_network_listen_outstanding(fr_network_t *nr, fr_listen_t *li)
Get the number of outstanding packets.
Definition network.c:857
int fr_ldap_conn_directory_alloc_async(fr_ldap_connection_t *ldap_conn)
Asynchronously extract useful information from the rootDSE of the LDAP server.
Definition directory.c:442
fr_ldap_sync_type_t sync_type
What kind of LDAP sync this directory supports.
Definition base.h:212
LDAP * handle
libldap handle.
Definition base.h:343
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition base.h:352
int fd
File descriptor for this connection.
Definition base.h:359
void fr_ldap_state_error(fr_ldap_connection_t *c)
Signal that there's been an error on the connection.
Definition state.c:153
int fr_ldap_server_url_check(fr_ldap_config_t *handle_config, char const *server, CONF_SECTION const *cs)
Check an LDAP server entry in URL format is valid.
Definition util.c:1036
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition base.h:354
int fr_ldap_server_config_check(fr_ldap_config_t *handle_config, char const *server, CONF_SECTION *cs)
Check an LDAP server config in server:port format is valid.
Definition util.c:1133
char const * name
Name of the module that created this connection.
Definition base.h:232
fr_time_delta_t reconnection_delay
How long to wait before attempting to reconnect.
Definition base.h:321
int fr_ldap_directory_result_parse(fr_ldap_directory_t *directory, LDAP *handle, LDAPMessage *result, char const *name)
Definition directory.c:68
void * uctx
User data associated with the handle.
Definition base.h:364
@ FR_LDAP_SYNC_NONE
No support for LDAP sync.
Definition base.h:158
@ FR_LDAP_SYNC_ACTIVE_DIRECTORY
Directory supports AD style persistent search.
Definition base.h:160
@ FR_LDAP_SYNC_PERSISTENT_SEARCH
Directory supports persistent search.
Definition base.h:161
@ FR_LDAP_SYNC_RFC4533
Directory supports RFC 4533.
Definition base.h:159
connection_t * conn
Connection state handle.
Definition base.h:355
char const ** naming_contexts
NULL terminated array of databases served by this directory.
Definition base.h:218
fr_ldap_rcode_t
Codes returned by fr_ldap internal functions.
Definition base.h:585
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition base.h:588
@ LDAP_PROC_BAD_CONN
Transitory error, caller should retry the operation with a new connection.
Definition base.h:592
@ LDAP_PROC_REFRESH_REQUIRED
Don't continue with the current refresh phase, exit, and retry the operation with a NULL cookie.
Definition base.h:607
Tracks the state of a libldap connection handle.
Definition base.h:342
#define FR_LDAP_COMMON_CONF(_conf)
Definition conf.h:19
fr_ldap_rcode_t fr_ldap_error_check(LDAPControl ***ctrls, fr_ldap_connection_t const *conn, LDAPMessage *msg, char const *dn)
Perform basic parsing of multiple types of messages, checking for error conditions.
Definition base.c:232
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
Definition base.c:1130
global_lib_autoinst_t fr_libldap_global_config
Definition base.c:134
fr_ldap_rcode_t fr_ldap_result(LDAPMessage **result, LDAPControl ***ctrls, fr_ldap_connection_t const *conn, int msgid, int all, char const *dn, fr_time_delta_t timeout)
Parse response from LDAP server dealing with any errors.
Definition base.c:450
#define PERROR(_fmt,...)
Definition log.h:233
#define FATAL(_fmt,...)
Definition log.h:229
#define DEBUG3(_fmt,...)
Definition log.h:271
fr_time_t fr_event_list_time(fr_event_list_t *el)
Get the current server time according to the event list.
Definition event.c:590
#define fr_time()
Definition event.c:60
bool fr_event_loop_exiting(fr_event_list_t *el)
Check to see whether the event loop is in the process of exiting.
Definition event.c:2393
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition event.c:1203
Stores all information relating to an event list.
Definition event.c:377
CONF_SECTION * cs
of this listener
Definition listen.h:41
char const * name
printable name for this socket - set by open
Definition listen.h:29
void const * app_instance
Definition listen.h:39
size_t default_message_size
copied from app_io, but may be changed
Definition listen.h:56
fr_app_t const * app
Definition listen.h:38
void const * app_io_instance
I/O path configuration context.
Definition listen.h:33
CONF_SECTION * server_cs
CONF_SECTION of the server.
Definition listen.h:42
void * thread_instance
thread / socket context
Definition listen.h:34
int fd
file descriptor for this socket - set by open
Definition listen.h:28
fr_app_io_t const * app_io
I/O path functions.
Definition listen.h:32
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_OCTETS
Raw octets.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
fr_cmp_ret_t
Result of an ordering comparison.
Definition misc.h:50
int strncasecmp(char *s1, char *s2, int n)
Definition missing.c:35
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
Duplicate a list of pairs.
Definition pair.c:2326
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition pair.c:784
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:707
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
int persistent_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
Handle a SearchResultEntry response from Persistent Search LDAP servers.
int persistent_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, proto_ldap_sync_t const *inst, UNUSED uint8_t const *cookie)
Allocate and initialise sync queries for persistent searches.
static const conf_parser_t config[]
Definition base.c:162
char const * filter
Filter to retrieve only user objects.
CONF_SECTION * server_cs
server CS for this listener.
fr_timer_t * ev
Event for retrying cookie load.
sync_config_t ** sync_config
DNs and filters to monitor.
int(* sync_msg_t)(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
Received an LDAP message related to a sync.
sync_op_t
Operations to perform on entries.
@ SYNC_OP_ADD
Entry should be added to our copy.
@ SYNC_OP_MODIFY
Entry should be updated in our copy.
@ SYNC_OP_DELETE
Entry should be deleted from our copy.
@ SYNC_OP_PRESENT
Entry is present and unchanged on the server.
char const * base_dn
DN to search for users under.
sync_msg_t entry
Called when we receive a searchEntry message.
sync_msg_t refresh
Called when we receive a eSyncRefreshRequired code.
CONF_SECTION * cs
Config section where this sync was defined.
sync_msg_t intermediate
Called when we receive a syncIntermediate message.
map_list_t entry_map
How to convert attributes in entries to FreeRADIUS attributes.
fr_pair_list_t sync_pairs
Pairs representing the sync config sent to the worker with each request.
fr_time_delta_t cookie_interval
Interval between storing cookies.
uint32_t cookie_changes
Number of LDAP changes to process between each cookie store operation.
fr_time_delta_t retry_interval
Interval between retrying failed change packets.
void * user_ctx
User ctx to pass to the callbacks.
An instance of a proto_ldap_sync listen section.
Areas of the directory to receive notifications for.
int ldap_sync_cookie_send(sync_packet_ctx_t *sync_packet_ctx)
Enqueue a new cookie store packet.
fr_app_io_t proto_ldap_sync_ldap
static void _proto_ldap_socket_closed(UNUSED connection_t *conn, connection_state_t prev, UNUSED connection_state_t state, void *uctx)
Callback for closure of LDAP connection.
static fr_dict_attr_t const * attr_packet_type
static fr_ldap_sync_packet_code_t const sync_packet_code_table[4]
static int proto_ldap_child_mod_close(fr_listen_t *li)
Child listener mod_close.
static int proto_ldap_cookie_load_send(TALLOC_CTX *ctx, proto_ldap_sync_ldap_t const *inst, size_t sync_no, proto_ldap_sync_ldap_thread_t *thread)
Send a fake packet to run the "load Cookie" section.
global_lib_autoinst_t const * proto_ldap_sync_ldap_lib[]
static fr_dict_attr_t const * attr_ldap_sync_packet_id
static void ldap_sync_retry_event(fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
Event to handle sending of any change packets which failed to send.
static fr_dict_t const * dict_ldap_sync
proto_ldap_sync_ldap_t const * inst
static fr_dict_attr_t const * attr_ldap_sync_entry_dn
int ldap_sync_cookie_store(sync_state_t *sync, bool refresh)
Add a new cookie packet ctx to the pending list.
static int sync_state_free(sync_state_t *sync)
Tell the remote server to stop the sync.
static fr_dict_t const * dict_freeradius
static fr_dict_attr_t const * attr_ldap_sync_base_dn
void ldap_sync_cookie_event(fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
Event to handle storing of cookies on a timed basis.
static fr_dict_attr_t const * attr_ldap_sync_orig_dn
static void mod_event_list_set(fr_listen_t *li, fr_event_list_t *el, void *nr)
Callback triggered when parent listener app_io has its event list set.
fr_app_io_t proto_ldap_sync_child
static conf_parser_t const proto_ldap_sync_ldap_config[]
static void _proto_ldap_socket_open_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, UNUSED int fd_errno, void *uctx)
Callback for socket errors when running initial root query.
int ldap_sync_entry_send(sync_state_t *sync, uint8_t const uuid[SYNC_UUID_LENGTH], struct berval *orig_dn, LDAPMessage *msg, sync_op_t op)
Enqueue a new entry change packet.
static ssize_t proto_ldap_child_mod_read(fr_listen_t *li, UNUSED void **packet_ctx, UNUSED fr_time_t *recv_time_p, UNUSED uint8_t *buffer, UNUSED size_t buffer_len, UNUSED size_t *leftover)
LDAP sync mod_read for child listener.
size_t sync_op_table_len
fr_table_num_sorted_t const sync_op_table[]
Operations performed on entries.
static fr_dict_attr_t const * attr_ldap_sync_root_dn
static fr_internal_encode_ctx_t encode_ctx
static int ldap_sync_entry_send_network(sync_packet_ctx_t *sync_packet_ctx)
Send a change packet to the workers.
static void proto_ldap_connection_init(fr_timer_list_t *tl, UNUSED fr_time_t now, void *user_ctx)
Attempt to (re)initialise a connection.
static fr_dict_attr_t const * attr_ldap_sync_entry_uuid
static void _proto_ldap_socket_init(connection_t *conn, UNUSED connection_state_t prev, UNUSED connection_state_t state, void *uctx)
Allocate a child listener.
sync_state_t * sync_state_alloc(TALLOC_CTX *ctx, fr_ldap_connection_t *conn, proto_ldap_sync_t const *inst, size_t sync_no, sync_config_t const *config)
Allocate a sync state.
fr_dict_attr_autoload_t proto_ldap_sync_ldap_dict_attr[]
fr_cmp_ret_t sync_state_cmp(void const *one, void const *two)
Compare two sync state structures on msgid.
static void proto_ldap_cookie_load_retry(fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
Timer event to retry running "load Cookie" on failures.
static fr_dict_attr_t const * attr_ldap_sync_cookie
proto_ldap_sync_ldap_thread_t * thread
fr_dict_autoload_t proto_ldap_sync_ldap_dict[]
static int mod_instantiate(module_inst_ctx_t const *mctx)
static void _proto_ldap_socket_open_read(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Callback to process results of initial root query, identifying directory type.
static void _proto_ldap_socket_open_connected(connection_t *conn, UNUSED connection_state_t prev, UNUSED connection_state_t state, void *uctx)
Query an LDAP server to establish its type.
static ssize_t proto_ldap_child_mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t request_time, uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
LDAP sync mod_write for child listener.
Context used when looking up Directory types.
uint32_t pending_cookies
How many cookies are in the pending heap.
uint8_t * cookie
Opaque cookie, used to resume synchronisation.
size_t sync_no
Array position of config for this sync.
@ SYNC_PACKET_TYPE_CHANGE
Packet is an entry change.
@ SYNC_PACKET_TYPE_COOKIE
sync_phases_t phase
Phase this sync is in.
uint32_t max_outstanding
Maximum number of outstanding packets.
int msgid
The unique identifier for this sync session.
uint8_t * cookie
Cookie to store - can be NULL.
fr_timer_t * retry_ev
Timer event for retrying failed changes.
fr_pair_list_t pairs
Pairs to send with change packets.
fr_dlist_head_t pending
List of pending changes in progress.
sync_config_t const * config
Configuration for this sync.
static fr_table_num_sorted_t const sync_ldap_msg_table[]
Types of LDAP messages relevant to LDAP sync.
sync_state_t * sync
Sync packet relates to.
sync_packet_status_t status
Status of this packet.
@ SYNC_PACKET_PREPARING
Packet being prepared.
@ SYNC_PACKET_PENDING
Packet not yet sent.
@ SYNC_PACKET_PROCESSING
Packet sent to worker.
@ SYNC_PACKET_COMPLETE
Packet response received from worker.
fr_listen_t * li
Our listener.
fr_timer_t * cookie_ev
Timer event for sending cookies.
connection_t * conn
Our connection to the LDAP directory.
proto_ldap_sync_t const * inst
Module instance for this sync.
fr_ldap_config_t handle_config
Connection configuration instance.
fr_network_t * nr
Network handler.
fr_listen_t * parent
master IO handler.
#define SYNC_UUID_LENGTH
proto_ldap_sync_t * parent
The module that spawned us.
bool refresh
Does the sync require a refresh.
@ SYNC_PHASE_INIT
We haven't entered any of the refresh phases.
sync_packet_type_t type
Type of packet.
uint32_t changes_since_cookie
How many changes have been added since the last cookie was stored.
fr_ldap_connection_t * conn
Connection the sync is running on.
fr_pair_list_t trigger_args
Arguments to make available in triggers.
fr_event_list_t * el
Network side event list.
fr_timer_t * conn_retry_ev
When to retry re-establishing the conn.
proto_ldap_sync_ldap_t const * inst
instance data
Tracking structure for ldap sync packets.
State of an individual sync.
ssize_t fr_internal_decode_list_dbuff(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr_t const *parent, fr_dbuff_t *dbuff, void *decode_ctx)
Retrieve all pairs from the dbuff.
Definition decode.c:314
ssize_t fr_internal_encode_list(fr_dbuff_t *dbuff, fr_pair_list_t const *list, void *encode_ctx)
Encode a list of pairs using the internal encoder.
Definition encode.c:304
#define fr_assert(_expr)
Definition rad_assert.h:37
#define DEBUG2(fmt,...)
#define WARN(fmt,...)
static rs_t * conf
Definition radsniff.c:52
int fr_rb_find(void **found, fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:586
int fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition rb.c:767
The main red black tree structure.
Definition rb.h:71
int rfc4533_sync_refresh_required(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
Handle result code of e-syncRefreshRequired.
Definition rfc4533.c:717
int rfc4533_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
Handle a SearchResultEntry or SearchResultReference response from an RFC 4533 server.
Definition rfc4533.c:260
int rfc4533_sync_init(fr_ldap_connection_t *conn, size_t sync_no, proto_ldap_sync_t const *inst, uint8_t const *cookie)
Allocate and initialise RFC 4533 sync queries.
Definition rfc4533.c:74
int rfc4533_sync_intermediate(sync_state_t *sync, LDAPMessage *msg, UNUSED LDAPControl **ctrls)
Handle a LDAP_RES_INTERMEDIATE (SyncInfo) response.
Definition rfc4533.c:452
void connection_signal_shutdown(connection_t *conn)
Shuts down a connection gracefully.
void connection_signal_halt(connection_t *conn)
Shuts down a connection ungracefully.
void connection_signal_reconnect(connection_t *conn, connection_reason_t reason)
Asynchronously signal the connection should be reconnected.
void connection_signal_init(connection_t *conn)
Asynchronously signal a halted connection to start.
connection_t * connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_funcs_t const *funcs, connection_conf_t const *conf, char const *log_prefix, void const *uctx)
Allocate a new connection.
connection_watch_entry_t * connection_add_watch_post(connection_t *conn, connection_state_t state, connection_watch_t watch, bool oneshot, void const *uctx)
Add a callback to be executed after a state function has been called.
Definition connection.c:542
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:351
void * data
Module's instance data.
Definition module.h:293
module_instance_t const * parent
Parent module's instance (if any).
Definition module.h:359
conf_parser_t const * config
How to convert a CONF_SECTION to a module instance.
Definition module.h:206
int pair_append_by_tmpl_parent(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, tmpl_t const *vpt, bool skip_list))
Allocate and insert a leaf vp from a tmpl_t, building the parent vps if needed.
Definition tmpl_eval.c:862
return count
Definition module.c:155
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
fr_pair_t * vp
Value pair map.
Definition map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition map.h:82
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition map.h:78
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition map.h:79
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_ldap_sync_packet_code_t
Types of the internal packets for processing LDAP sync messages.
Definition sync.h:31
@ FR_LDAP_SYNC_CODE_PRESENT
LDAP server indicates a particular object is present and unchanged.
Definition sync.h:33
@ FR_LDAP_SYNC_CODE_COOKIE_STORE_RESPONSE
Response to storing the new cookie.
Definition sync.h:52
@ FR_LDAP_SYNC_CODE_ENTRY_RESPONSE
Response packet to present / add / modify / delete.
Definition sync.h:42
@ FR_LDAP_SYNC_CODE_COOKIE_LOAD_FAIL
Response when coolie load fails.
Definition sync.h:48
@ FR_LDAP_SYNC_CODE_ADD
Object has been added to the LDAP directory.
Definition sync.h:36
@ FR_LDAP_SYNC_CODE_COOKIE_STORE
The server has sent a new cookie.
Definition sync.h:50
@ FR_LDAP_SYNC_CODE_COOKIE_LOAD_RESPONSE
Response with the returned cookie.
Definition sync.h:46
@ FR_LDAP_SYNC_CODE_DELETE
Object has been deleted.
Definition sync.h:40
@ FR_LDAP_SYNC_CODE_COOKIE_LOAD
Before the sync starts, request any previously stored cookie.
Definition sync.h:44
@ FR_LDAP_SYNC_CODE_MODIFY
Object has been modified.
Definition sync.h:38
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition talloc.c:546
#define talloc_get_type_abort_const
Definition talloc.h:117
static size_t talloc_str_array_len(char const *const *strings)
Return the number of strings a NULL terminated string array was sized for.
Definition talloc.h:279
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
static fr_time_delta_t fr_time_delta_from_msec(int64_t msec)
Definition time.h:575
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:49
#define fr_timer_in(...)
Definition timer.h:87
@ T_OP_ADD_EQ
Definition token.h:67
int trigger(unlang_interpret_t *intp, CONF_SECTION const *cs, CONF_PAIR **trigger_cp, char const *name, bool rate_limit, fr_pair_list_t *args, void const *uctx)
Execute a trigger - call an executable to process an event.
Definition trigger.c:160
static fr_event_list_t * el
#define fr_pair_list_append_by_da_len(_ctx, _vp, _list, _attr, _val, _len, _tainted)
Append a pair to a list, assigning its value.
Definition pair.h:327
fr_pair_t * fr_pair_remove(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list without freeing.
Definition pair_inline.c:93
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
#define fr_pair_list_append_by_da(_ctx, _vp, _list, _attr, _val, _tainted)
Append a pair to a list, assigning its value.
Definition pair.h:304
#define fr_pair_list_append_by_da_parent_len(_ctx, _vp, _list, _attr, _val, _len, _tainted)
Definition pair.h:349
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules)
Definition value.c:6094
#define fr_box_time_delta(_val)
Definition value.h:366