The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 8c0d0e0866b5c80b14aabab813f319048c0e8ea7 $
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/protocol.h>
31#include <freeradius-devel/server/request.h>
32#include <freeradius-devel/io/listen.h>
33#include <freeradius-devel/io/application.h>
34#include <freeradius-devel/unlang/call.h>
35#include <freeradius-devel/util/dbuff.h>
36#include <freeradius-devel/ldap/base.h>
37#include <freeradius-devel/ldap/conf.h>
38
40#include "rfc4533.h"
41#include "persistent_search.h"
42#include "active_directory.h"
43
45
48
50 /*
51 * LDAP server definition
52 */
54
55 /*
56 * Common LDAP conf parsers
57 */
59
60 /*
61 * Network tunable parameters
62 */
63 { FR_CONF_OFFSET_IS_SET("recv_buff", FR_TYPE_UINT32, 0, proto_ldap_sync_ldap_t, recv_buff) },
64 { FR_CONF_OFFSET("max_outstanding", proto_ldap_sync_ldap_t, max_outstanding), .dflt = "65536" },
65
67};
68
71
74 { .out = &dict_ldap_sync, .proto = "ldap" },
75 { .out = &dict_freeradius, .proto = "freeradius" },
76 { NULL }
77};
78
87
90 { .out = &attr_ldap_sync_packet_id, .name = "Sync-Packet-ID", .type = FR_TYPE_UINT32, .dict = &dict_ldap_sync },
91 { .out = &attr_ldap_sync_cookie, .name = "LDAP-Sync.Cookie", .type = FR_TYPE_OCTETS, .dict = &dict_ldap_sync },
92 { .out = &attr_ldap_sync_entry_dn, .name = "LDAP-Sync.Entry-DN", .type = FR_TYPE_STRING, .dict = &dict_ldap_sync },
93 { .out = &attr_ldap_sync_entry_uuid, .name = "LDAP-Sync.Entry-UUID", .type = FR_TYPE_OCTETS, .dict = &dict_ldap_sync },
94 { .out = &attr_ldap_sync_orig_dn, .name = "LDAP-Sync.Original-DN", .type = FR_TYPE_STRING, .dict = &dict_ldap_sync },
95 { .out = &attr_ldap_sync_root_dn, .name = "LDAP-Sync.Directory-Root-DN", .type = FR_TYPE_STRING, .dict = &dict_ldap_sync },
96 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_ldap_sync },
97 { .out = &attr_ldap_sync_base_dn, .name = "LDAP-Sync-Base-DN", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
98 { NULL }
99};
100
106
107/** Operations performed on entries
108 */
110 { L("add"), SYNC_OP_ADD },
111 { L("delete"), SYNC_OP_DELETE },
112 { L("modify"), SYNC_OP_MODIFY },
113 { L("present"), SYNC_OP_PRESENT },
114};
116
117/** Context used when looking up Directory types
118 */
125
126/** Context for "load Cookie" retry timed event
127 */
133
134/** Compare two sync state structures on msgid
135 *
136 * @param[in] one first sync to compare.
137 * @param[in] two second sync to compare.
138 * @return CMP(one, two)
139 */
140int8_t sync_state_cmp(void const *one, void const *two)
141{
142 sync_state_t const *a = one, *b = two;
143
144 return CMP(a->msgid, b->msgid);
145}
146
147/** Tell the remote server to stop the sync
148 *
149 * Terminates the search informing the remote server that we no longer want to receive results
150 * for this sync. A RFC 4511 abandon request is used to inform the server.
151 *
152 * This allows individual syncs to be stopped without destroying the underlying connection.
153 *
154 * Removes the sync's msgid from the tree of msgids associated with the connection.
155 *
156 * @param[in] sync to abandon.
157 * @return 0
158 */
160{
161 fr_ldap_connection_t *conn = talloc_get_type_abort(sync->conn, fr_ldap_connection_t);
162 fr_rb_tree_t *tree = talloc_get_type_abort(conn->uctx, fr_rb_tree_t);
163
164 DEBUG3("Abandoning sync base dn \"%s\", filter \"%s\"", sync->config->base_dn, sync->config->filter);
165
166 trigger_exec(NULL, sync->config->cs, "ldap_sync.stop", true, &sync->trigger_args);
167
168 if (!sync->conn->handle) return 0; /* Handled already closed? */
169
170 /*
171 * Tell the remote server to stop sending results
172 */
173 if (sync->msgid >= 0) ldap_abandon_ext(sync->conn->handle, sync->msgid, NULL, NULL);
174 fr_rb_delete(tree, &(sync_state_t){.msgid = sync->msgid});
175
176 return 0;
177}
178
179/** Allocate a sync state
180 *
181 * @param[in] ctx to allocate the sync state in.
182 * @param[in] conn which the sync will run on.
183 * @param[in] inst module instance for the sync.
184 * @param[in] sync_no number of the sync in the array of configs.
185 * @param[in] config for the sync.
186 * @return new sync state.
187 */
189 size_t sync_no, sync_config_t const *config)
190{
191 sync_state_t *sync;
192 fr_pair_t *vp;
193
194 MEM(sync = talloc_zero(ctx, sync_state_t));
195 sync->conn = conn;
196 sync->inst = inst;
197 sync->config = config;
198 sync->sync_no = sync_no;
199 sync->phase = SYNC_PHASE_INIT;
200
202
203 /*
204 * Create arguments to pass to triggers
205 */
208 talloc_array_length(config->base_dn) - 1, false);
209
210 /*
211 * If the connection is freed, all the sync state is also freed
212 */
213 talloc_set_destructor(sync, sync_state_free);
214
215 return sync;
216}
217
218/** Add a new cookie packet ctx to the pending list
219 *
220 * Does not actually send the packet.
221 *
222 * @param[in] sync the cookie was received for.
223 * @param[in] refresh the sync after storing this cookie.
224 * @return
225 * - 0 on success.
226 * - -1 on failure
227 */
228int ldap_sync_cookie_store(sync_state_t *sync, bool refresh)
229{
230 sync_packet_ctx_t *sync_packet_ctx = NULL;
231 uint8_t *cookie = sync->cookie;
232
233 MEM(sync_packet_ctx = talloc_zero(sync, sync_packet_ctx_t));
234 sync_packet_ctx->sync = sync;
235
236 sync_packet_ctx->type = SYNC_PACKET_TYPE_COOKIE;
237 if (cookie) sync_packet_ctx->cookie = talloc_memdup(sync_packet_ctx, cookie, talloc_array_length(cookie));
238 sync_packet_ctx->refresh = refresh;
239
240 if (fr_dlist_insert_tail(&sync->pending, sync_packet_ctx) < 0) {
241 talloc_free(sync_packet_ctx);
242 return -1;
243 }
244 sync->pending_cookies++;
245
246 return 0;
247}
248
249/** Event to handle storing of cookies on a timed basis
250 *
251 * Looks at the head of the list of pending sync packets for a cookie.
252 * A cookie at the head says that all the previous changes have been
253 * completed, so the cookie can be sent.
254 */
256{
257 sync_state_t *sync = talloc_get_type_abort(uctx, sync_state_t);
258 sync_packet_ctx_t *sync_packet_ctx;
259
260 if (sync->pending_cookies == 0) goto finish;
261
262 /*
263 * Check the head entry in the list - is it a pending cookie
264 */
265 sync_packet_ctx = fr_dlist_head(&sync->pending);
266 if ((sync_packet_ctx->type != SYNC_PACKET_TYPE_COOKIE) ||
267 (sync_packet_ctx->status != SYNC_PACKET_PENDING)) goto finish;
268
269 ldap_sync_cookie_send(sync_packet_ctx);
270
271finish:
272 (void) fr_event_timer_in(sync, el, &sync->cookie_ev, sync->inst->cookie_interval, ldap_sync_cookie_event, sync);
273}
274
275/** Enqueue a new cookie store packet
276 *
277 * Create a new internal packet containing the cookie we received from the LDAP server.
278 * This allows the administrator to store the cookie and provide it on a future call to
279 * load Cookie.
280 *
281 * @param[in] sync_packet_ctx packet context containing the cookie to store.
282 * @return
283 * - 0 on success.
284 * - -1 on failure.
285*/
287{
288 sync_state_t *sync = sync_packet_ctx->sync;
289 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(sync->config->user_ctx, proto_ldap_sync_ldap_thread_t);
290 fr_dbuff_t *dbuff;
291 fr_pair_list_t pairs;
292 fr_pair_t *vp;
293 TALLOC_CTX *local = NULL;
294 uint8_t *cookie = sync_packet_ctx->cookie;
295
296 if (sync_packet_ctx->status != SYNC_PACKET_PENDING) return 0;
297 sync_packet_ctx->status = SYNC_PACKET_PREPARING;
298
299 FR_DBUFF_TALLOC_THREAD_LOCAL(&dbuff, 1024, 4096);
300
301 local = talloc_new(NULL);
302 fr_pair_list_init(&pairs);
303 if (fr_pair_list_copy(local, &pairs, &sync->config->sync_pairs) < 0) {
304 error:
305 talloc_free(local);
306 return -1;
307 }
308
310 if (!vp) goto error;
311
313 if (!vp) goto error;
314
315 /*
316 * Add the cookie to the packet, if set.
317 * If the server has indicated a refresh is required it can do so
318 * with no cookie set - so we store a blank cookie to clear anything
319 * which was previously stored.
320 */
321 if (cookie) {
323 cookie, talloc_array_length(cookie), true);
324 if (!vp) goto error;
325 }
326
327 if (fr_internal_encode_list(dbuff, &pairs, &encode_ctx) < 0) goto error;
328 talloc_free(local);
329
330 if (fr_network_listen_send_packet(thread->nr, thread->li, thread->li, fr_dbuff_buff(dbuff),
331 fr_dbuff_used(dbuff), fr_time(), sync_packet_ctx) < 0) {
332 sync_packet_ctx->status = SYNC_PACKET_PENDING;
333 return -1;
334 }
335
336 sync_packet_ctx->status = SYNC_PACKET_PROCESSING;
337
338 return 0;
339}
340
341/** Send a change packet to the workers
342 *
343 * Called each time a change packet is received and also from a
344 * timer event retrying packets which previously failed to send.
345 *
346 * @param sync_packet_ctx Packet to send
347 * @return
348 * - 0 on success.
349 * - -1 on failure.
350 */
352{
353 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(sync_packet_ctx->sync->config->user_ctx,
355 fr_dbuff_t *dbuff;
356
357 FR_DBUFF_TALLOC_THREAD_LOCAL(&dbuff, 1024, 4096);
358
359 if (fr_internal_encode_list(dbuff, &sync_packet_ctx->pairs, &encode_ctx) < 0) return -1;
360 if (fr_network_listen_send_packet(thread->nr, thread->li, thread->li, fr_dbuff_buff(dbuff),
361 fr_dbuff_used(dbuff), fr_time(), sync_packet_ctx) < 0) return -1;
362
363 sync_packet_ctx->status = SYNC_PACKET_PROCESSING;
364 fr_pair_list_free(&sync_packet_ctx->pairs);
365
366 return 0;
367}
368
369/** Event to handle sending of any change packets which failed to send.
370 *
371 * Looks at the head of the list of pending sync packets for unsent
372 * change packets and sends any up to the first cookie.
373 */
375{
376 sync_state_t *sync = talloc_get_type_abort(uctx, sync_state_t);
377 sync_packet_ctx_t *sync_packet_ctx = NULL;
378
379 while ((sync_packet_ctx = fr_dlist_next(&sync->pending, sync_packet_ctx))) {
380 if (sync_packet_ctx->type != SYNC_PACKET_TYPE_CHANGE) break;
381 if (sync_packet_ctx->status != SYNC_PACKET_PENDING) continue;
382
383 /*
384 * Retry sending packet. Don't try any more if it fails.
385 */
386 if (ldap_sync_entry_send_network(sync_packet_ctx) < 0) break;
387 }
388
389 /*
390 * We didn't run through the whole list, so there may be other pending
391 * packets - reschedule a retry event.
392 */
393 if (sync_packet_ctx) {
394 (void) fr_event_timer_in(sync, el, &sync->retry_ev, sync->inst->retry_interval,
396 }
397}
398
405
406/** Enqueue a new entry change packet.
407 *
408 * @param[in] sync notification has arrived for.
409 * @param[in] uuid of the entry (RFC 4533 only).
410 * @param[in] orig_dn original DN of the entry - provided by those directories
411 * implementing persistent search, when an entry is renamed.
412 * @param[in] msg containing the entry.
413 * @param[in] op The type of modification we need to perform to our
414 * representation of the entry.
415 * @return
416 * - 0 on success.
417 * - -1 on failure.
418 */
419int ldap_sync_entry_send(sync_state_t *sync, uint8_t const uuid[SYNC_UUID_LENGTH], struct berval *orig_dn,
420 LDAPMessage *msg, sync_op_t op)
421{
423 fr_pair_list_t *pairs;
424 fr_pair_t *vp;
425 sync_packet_ctx_t *sync_packet_ctx = NULL;
426
427 MEM(sync_packet_ctx = talloc_zero(sync, sync_packet_ctx_t));
428 sync_packet_ctx->sync = sync;
429
430 fr_pair_list_init(&sync_packet_ctx->pairs);
431 pairs = &sync_packet_ctx->pairs;
432
433 if (fr_pair_list_copy(sync_packet_ctx, pairs, &sync->config->sync_pairs) < 0) {
434 error:
435 if (msg) ldap_msgfree(msg);
436 talloc_free(sync_packet_ctx);
437 return -1;
438 }
439
440 pcode = sync_packet_code_table[op];
441
442 fr_pair_list_append_by_da(sync_packet_ctx, vp, pairs, attr_packet_type, (uint32_t)pcode, false);
443 if (!vp) goto error;
444
445 fr_pair_list_append_by_da(sync_packet_ctx, vp, pairs, attr_ldap_sync_packet_id, (uint32_t)sync->sync_no, false);
446 if (!vp) goto error;
447
448 /*
449 * Add the UUID if provided
450 */
451 if (uuid) {
453 uuid, SYNC_UUID_LENGTH, true);
454 if (!vp) goto error;
455 }
456
457 /*
458 * Add the original DN if provided
459 */
460 if (orig_dn && (orig_dn->bv_len > 0)) {
462 orig_dn->bv_val, orig_dn->bv_len, true);
463 if (!vp) goto error;
464 }
465
466 /*
467 * Add the entry DN if there is an LDAP message to read
468 */
469 if (msg) {
470 char *entry_dn = ldap_get_dn(sync->conn->handle, msg);
471 map_t const *map = NULL;
472 struct berval **values;
473 int count, i;
474
476 entry_dn, strlen(entry_dn), true);
477 if (!vp) goto error;
478
479 ldap_memfree(entry_dn);
480
481 /*
482 * Map LDAP returned attributes to pairs as per update map
483 */
484 while ((map = map_list_next(&sync->config->entry_map, map))) {
485 values = ldap_get_values_len(fr_ldap_handle_thread_local(), msg, map->rhs->name);
486 if (!values) goto next;
487
488 count = ldap_count_values_len(values);
489
490 for (i = 0; i < count; i++) {
491 if (values[i]->bv_len == 0) continue;
492
493 if (pair_append_by_tmpl_parent(sync_packet_ctx, &vp, pairs, map->lhs, true) < 0) break;
494 if (fr_value_box_from_str(vp, &vp->data, vp->vp_type, NULL, values[i]->bv_val,
495 values[i]->bv_len, NULL, true) < 0) {
496 fr_pair_remove(pairs, vp);
498 }
499
500 /* Only += operator adds multiple values */
501 if (map->op != T_OP_ADD_EQ) break;
502 }
503 next:
504 ldap_value_free_len(values);
505 }
506 }
507
508 if (fr_dlist_insert_tail(&sync->pending, sync_packet_ctx) < 0) goto error;
509
510 ldap_msgfree(msg);
511
512 /*
513 * Send the packet and if it fails to send add a retry event
514 */
515 if ((ldap_sync_entry_send_network(sync_packet_ctx) < 0) &&
516 (fr_event_timer_in(sync, sync->conn->conn->el, &sync->retry_ev,
517 sync->inst->retry_interval, ldap_sync_retry_event, sync) < 0)) {
518 PERROR("Inserting LDAP sync retry timer failed");
519 }
520
521 return 0;
522}
523
525 UNUSED connection_state_t state, void *uctx);
526
528 UNUSED connection_state_t state, void *uctx);
529
530/** Attempt to (re)initialise a connection
531 *
532 * Performs complete re-initialization of a connection. Called during socket_open
533 * to create the initial connection and again any time we need to reopen the connection.
534 *
535 * @param[in] el the event list managing listen event.
536 * @param[in] now current time.
537 * @param[in] user_ctx Listener.
538 */
540{
541 fr_listen_t *listen = talloc_get_type_abort(user_ctx, fr_listen_t);
542 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(listen->thread_instance, proto_ldap_sync_ldap_thread_t);
544
545 if (thread->conn) talloc_free(thread->conn);
546
547 /*
548 * Allocate an outbound LDAP connection
549 */
550 thread->conn = fr_ldap_connection_state_alloc(thread, thread->el, &inst->handle_config, "ldap_sync");
551
552 if (!thread->conn) {
553 PERROR("Failed (re)initialising connection, will retry in %pV seconds",
554 fr_box_time_delta(inst->handle_config.reconnection_delay));
555
556 if (fr_event_timer_in(thread, thread->el, &thread->conn_retry_ev,
557 inst->handle_config.reconnection_delay,
558 proto_ldap_connection_init, listen) < 0) {
559 FATAL("Failed inserting event: %s", fr_strerror());
560 }
561 }
562
563 /*
564 * Add watch functions on the LDAP connection
565 */
567 _proto_ldap_socket_init, true, thread);
568
571
572 /*
573 * Signal the connection to start
574 */
576
577 return;
578}
579
580/** Child listener mod_close
581 *
582 * Ensures the LDAP connection is signalled to close gracefully when
583 * the listener is closed.
584 */
586{
588
590 return 0;
591}
592
593/** LDAP sync mod_read for child listener
594 *
595 * Called when there is data to read on the LDAP connection
596 *
597 * Actual packets are created by the various callbacks since a single LDAP
598 * message can result in multiple packets to process e.g.:
599 *
600 * - Sync Info Message with syncInfoValue of syncIdSet can reference
601 * multiple directory entries.
602 * - Various sync related messages can include a new cookie in
603 * addition to their other data.
604 */
606 UNUSED size_t buffer_len, UNUSED size_t *leftover)
607{
609 fr_ldap_connection_t *conn = talloc_get_type_abort(thread->conn->h, fr_ldap_connection_t);
610 struct timeval poll = { 1, 0 };
611 LDAPMessage *msg = NULL;
612 int ret = 0;
613 fr_ldap_rcode_t rcode;
614 sync_state_t *sync = NULL;
615 fr_rb_tree_t *tree;
616 int type, msgid;
617 LDAPControl **ctrls = NULL;
618 sync_msg_t callback = NULL;
619
620 fr_assert(conn);
621
622 /*
623 * If there are already too many outstanding requests just return.
624 * This will (potentially) cause the TCP buffer to fill and push the
625 * backpressure back to the LDAP server.
626 */
627 if (fr_network_listen_outstanding(thread->nr, li) >= thread->inst->max_outstanding) return 0;
628
629 tree = talloc_get_type_abort(conn->uctx, fr_rb_tree_t);
630
631 /*
632 * Pull the next outstanding message from this connection.
633 * We process one message at a time so that the message can be
634 * passed to the worker, and freed once the request has been
635 * handled.
636 */
637 ret = ldap_result(conn->handle, LDAP_RES_ANY, LDAP_MSG_ONE, &poll, &msg);
638
639 switch (ret) {
640 case 0: /*
641 * Timeout - this has been observed if changes are being
642 * processed slowly, the TCP receive buffer fills and
643 * the LDAP directory pauses sending data for a period.
644 * Then all pending changes are processed and the receive buffer
645 * is emptied.
646 * The situation resolves when the directory starts sending
647 * data again.
648 */
649 return 0;
650
651 case -1:
652 rcode = fr_ldap_error_check(NULL, conn, NULL, NULL);
653 if (rcode == LDAP_PROC_BAD_CONN) return -2;
654 return -1;
655
656 default:
657 break;
658 }
659
660 /*
661 * De-multiplex based on msgid
662 */
663 if (!msg) return 0;
664
665 msgid = ldap_msgid(msg);
666 type = ldap_msgtype(msg);
667
668 ret = 0;
669 if (msgid == 0) {
670 WARN("Ignoring unsolicited %s message",
672 free_msg:
673 if (ctrls) ldap_controls_free(ctrls);
674 ldap_msgfree(msg);
675 return ret;
676 }
677
678 sync = fr_rb_find(tree, &(sync_state_t){.msgid = msgid});
679 if (!sync) {
680 WARN("Ignoring msgid %i, doesn't match any outstanding syncs", msgid);
681 goto free_msg;
682 }
683
684 /*
685 * Check for errors contained within the message.
686 * This has to be per message, as multiple syncs
687 * are multiplexed together on one connection.
688 */
689 switch (fr_ldap_error_check(&ctrls, conn, msg, sync->config->base_dn)) {
691 break;
692
693 /*
694 * The e-syncRefresRequired result code is the server informing us that
695 * the query needs to be restarted for a new refresh phase to run.
696 * It is sent as the result code for a SearchResultsDone message.
697 */
699 if (type != LDAP_RES_SEARCH_RESULT) {
700 PERROR("e-syncRefreshRequired result code received on wrong message type");
701 ret = -1;
702 goto free_msg;
703 }
704
705 DEBUG2("LDAP Server returned e-syncRefreshRequired");
706 if (sync->config->refresh) {
707 return sync->config->refresh(sync, msg, ctrls);
708 }
709 goto free_msg;
710
711 /*
712 * Don't think this should happen... but libldap
713 * is wonky sometimes...
714 */
716 PERROR("Connection unusable");
717 ret = -2;
718 goto free_msg;
719
720 default:
721 PERROR("Sync error");
722 ret = -1;
723 goto free_msg;
724 }
725
726 DEBUG3("Got %s message for sync (msgid %i)",
728
729 switch (type) {
730 case LDAP_RES_SEARCH_REFERENCE:
731 case LDAP_RES_SEARCH_ENTRY:
732 callback = sync->config->entry;
733 break;
734
735 case LDAP_RES_INTERMEDIATE:
736 callback = sync->config->intermediate;
737 break;
738
739 default:
740 WARN("Ignoring unexpected message type (%i)", type);
741 ret = 0;
742 goto free_msg;
743 }
744
745 if (callback) {
746 ret = callback(sync, msg, ctrls);
747 if (ret < 0) PERROR("Sync callback error");
748 } else {
749 /*
750 * Callbacks are responsible for freeing the msg
751 * so if there is no callback, free it.
752 */
753 ldap_msgfree(msg);
754 }
755
756 ldap_controls_free(ctrls);
757
758 return ret;
759}
760
761/** Send a fake packet to run the "load Cookie" section
762 *
763 * @param ctx Context to allocate temporary pairs in.
764 * @param inst LDAP sync configuration.
765 * @param sync_no Id of the sync whose.
766 * @param thread Thread specific LDAP sync data.
767 * @return
768 * - 0 on success
769 * - -1 on failure
770 */
771static int proto_ldap_cookie_load_send(TALLOC_CTX *ctx, proto_ldap_sync_ldap_t const *inst, size_t sync_no,
773 size_t j, len;
774 sync_config_t *config = inst->parent->sync_config[sync_no];
775 fr_pair_list_t pairs;
776 fr_pair_t *vp;
777 fr_dbuff_t *dbuff;
778 fr_ldap_connection_t *ldap_conn = thread->conn->h;
779
780 fr_pair_list_init(&pairs);
781 if (unlikely(fr_pair_list_copy(ctx, &pairs, &config->sync_pairs) < 0)) return -1;
782
783 /*
784 * Ensure we have access to the thread instance
785 * in for the demux callbacks
786 */
787 inst->parent->sync_config[sync_no]->user_ctx = thread;
788
789 /*
790 * Assess the namingContext which applies to this sync
791 */
792 for (j = 0; j < talloc_array_length(ldap_conn->directory->naming_contexts); j++) {
793 len = strlen(ldap_conn->directory->naming_contexts[j]);
794 if (strlen(config->base_dn) < len) continue;
795
796 if (strncasecmp(&config->base_dn[strlen(config->base_dn)-len],
797 ldap_conn->directory->naming_contexts[j],
798 strlen(ldap_conn->directory->naming_contexts[j])) == 0) {
799 config->root_dn = ldap_conn->directory->naming_contexts[j];
800 break;
801 }
802 }
803
804 /*
805 * Set up callbacks based on directory type.
806 */
807 switch (ldap_conn->directory->sync_type) {
811 config->intermediate = rfc4533_sync_intermediate;
813 break;
814
818 break;
819
823 break;
824
825 default:
826 fr_assert(0);
827 }
828
831 if (!vp) return -1;
832 fr_pair_list_append_by_da(ctx, vp, &pairs, attr_ldap_sync_packet_id, (uint32_t)sync_no, false);
833 if (!vp) return -1;
834
835 if (config->root_dn) {
837 config->root_dn, strlen(config->root_dn), false);
838 if (!vp) return -1;
839 }
840
841 FR_DBUFF_TALLOC_THREAD_LOCAL(&dbuff, 1024, 4096);
842
843 if (fr_internal_encode_list(dbuff, &pairs, &encode_ctx) < 0) return -1;
844
845 if (fr_network_listen_send_packet(thread->nr, thread->li, thread->li,
846 fr_dbuff_buff(dbuff), fr_dbuff_used(dbuff),
847 fr_time(), NULL) < 0) return -1;
848 fr_pair_list_free(&pairs);
849 return 0;
850}
851
852/** Timer event to retry running "load Cookie" on failures
853 *
854 */
856 proto_ldap_cookie_load_retry_ctx *retry_ctx = talloc_get_type_abort(uctx, proto_ldap_cookie_load_retry_ctx);
857
858 DEBUG2("Retrying \"load Cookie\" for sync no %ld", retry_ctx->sync_no);
859 if (proto_ldap_cookie_load_send(retry_ctx, retry_ctx->inst, retry_ctx->sync_no,
860 retry_ctx->thread) < 0) {
861 ERROR("Failed retrying \"load Cookie\". Will try again in %pV seconds",
863 (void) fr_event_timer_in(retry_ctx->thread->conn->h, el,
864 &retry_ctx->inst->parent->sync_config[retry_ctx->sync_no]->ev,
867 return;
868 }
869 talloc_free(retry_ctx);
870}
871
872/** LDAP sync mod_write for child listener
873 *
874 * Handle any returned data after the worker has processed the packet and,
875 * for packets where tracking structures were used, ensure they are freed.
876 */
877static ssize_t proto_ldap_child_mod_write(fr_listen_t *li, void *packet_ctx, UNUSED fr_time_t request_time,
878 uint8_t *buffer, size_t buffer_len, UNUSED size_t written)
879{
882 fr_dbuff_t dbuff;
884 uint32_t packet_id;
885 fr_pair_list_t tmp;
886 fr_pair_t *vp = NULL;
887 ssize_t ret;
888 TALLOC_CTX *local;
889 sync_packet_ctx_t *sync_packet_ctx = NULL;
890
891 local = talloc_new(NULL);
892 fr_dbuff_init(&dbuff, buffer, buffer_len);
893
894 if (packet_ctx) sync_packet_ctx = talloc_get_type_abort(packet_ctx, sync_packet_ctx_t);
895
896 /*
897 * Extract returned attributes into a temporary list
898 */
899 fr_pair_list_init(&tmp);
900
901 ret = fr_internal_decode_list_dbuff(local, &tmp, fr_dict_root(dict_ldap_sync), &dbuff, NULL);
902 if (ret < 0) goto finish;
903
904 /*
905 * There should always be a packet ID and code
906 */
908 fr_assert(vp);
909 packet_id = vp->vp_uint32;
910
912 fr_assert(vp);
913 pcode = vp->vp_uint32;
914
915 switch (pcode) {
917 {
918 uint8_t *cookie = NULL;
919
920 /*
921 * If the received packet ID is greater than the number of syncs
922 * we have then something very bad has happened
923 */
924 fr_assert (packet_id <= talloc_array_length(inst->parent->sync_config));
925
926 /*
927 * Look for the returned cookie.
928 */
930 if (vp) cookie = talloc_memdup(inst, vp->vp_octets, vp->vp_length);
931
932 if (inst->parent->sync_config[packet_id]->init(thread->conn->h, packet_id, inst->parent, cookie) < 0) {
933 ret = -1;
934 goto finish;
935 }
936 }
937 break;
938
940 break;
941
943 {
944 sync_config_t const *sync_config;
945
946 if (!sync_packet_ctx || !sync_packet_ctx->refresh) break;
947
948 /*
949 * Abandon the old sync and start a new one with the relevant cookie.
950 */
951 sync_config = sync_packet_ctx->sync->config;
952 DEBUG3("Restarting sync with base %s", sync_config->base_dn);
953 talloc_free(sync_packet_ctx->sync);
954 if (inst->parent->sync_config[packet_id]->init(thread->conn->h, packet_id, inst->parent,
955 sync_packet_ctx->cookie) < 0) {
956 ret = -1;
957 goto finish;
958 }
959 }
960 break;
961
963 {
965
966 ERROR("Load Cookie failed for sync %d, retrying in %pV seconds", packet_id,
967 fr_box_time_delta(inst->handle_config.reconnection_delay));
968
969 MEM(retry_ctx = talloc(thread, proto_ldap_cookie_load_retry_ctx));
971 .thread = thread,
972 .inst = inst,
973 .sync_no = packet_id,
974 };
975
976 (void) fr_event_timer_in(thread->conn->h, thread->el, &inst->parent->sync_config[packet_id]->ev,
977 inst->handle_config.reconnection_delay,
979 }
980 break;
981
982 default:
983 ERROR("Invalid packet type returned %d", pcode);
984 break;
985 }
986
987 if (sync_packet_ctx) {
988 sync_state_t *sync = sync_packet_ctx->sync;
990 proto_ldap_sync_t *ldap_sync = inst->parent;
991
992 sync_packet_ctx->status = SYNC_PACKET_COMPLETE;
993
994 /*
995 * A cookie has been stored, reset the counter of changes
996 */
997 if (sync_packet_ctx->type == SYNC_PACKET_TYPE_COOKIE) sync->changes_since_cookie = 0;
998
999 /*
1000 * Pop any processed updates from the head of the list
1001 */
1002 while ((pc = fr_dlist_head(&sync->pending))) {
1003 /*
1004 * If the head entry in the list is a pending cookie but we have
1005 * not processed enough entries and there are more pending
1006 * cookies, mark this one as processed.
1007 */
1008 if ((pc->type == SYNC_PACKET_TYPE_COOKIE) && (pc->status == SYNC_PACKET_PENDING) &&
1009 (sync->changes_since_cookie < ldap_sync->cookie_changes) &&
1010 (sync->pending_cookies > 1)) pc->status = SYNC_PACKET_COMPLETE;
1011
1012 if (pc->status != SYNC_PACKET_COMPLETE) break;
1013
1014 /*
1015 * Update counters depending on entry type
1016 */
1017 if (pc->type == SYNC_PACKET_TYPE_COOKIE) {
1018 sync->pending_cookies--;
1019 } else {
1020 sync->changes_since_cookie++;
1021 }
1022 pc = fr_dlist_pop_head(&sync->pending);
1023 talloc_free(pc);
1024 }
1025
1026 /*
1027 * If the head of the list is a cookie which has not yet
1028 * been processed and sufficient changes have been recorded
1029 * send the cookie.
1030 */
1031 if (pc && (pc->type == SYNC_PACKET_TYPE_COOKIE) && (pc->status == SYNC_PACKET_PENDING) &&
1033 }
1034
1035finish:
1036 fr_pair_list_free(&tmp);
1037 talloc_free(local);
1038
1039 return ret;
1040}
1041
1042/** Callback for socket errors when running initial root query
1043 */
1045 UNUSED int fd_errno, void *uctx)
1046{
1047 proto_ldap_dir_ctx *dir_ctx = talloc_get_type_abort(uctx, proto_ldap_dir_ctx);
1048 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(dir_ctx->conn->h, fr_ldap_connection_t);
1049
1050 talloc_free(dir_ctx);
1051 fr_ldap_state_error(ldap_conn);
1052}
1053
1054/** Callback to process results of initial root query, identifying directory type
1055 */
1056static void _proto_ldap_socket_open_read(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
1057{
1058 proto_ldap_dir_ctx *dir_ctx = talloc_get_type_abort(uctx, proto_ldap_dir_ctx);
1059 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(dir_ctx->conn->h, fr_ldap_connection_t);
1062 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(dir_ctx->main_listen->thread_instance,
1064 fr_ldap_rcode_t status;
1065 LDAPMessage *result;
1066
1067 size_t i;
1068 TALLOC_CTX *local = NULL;
1069
1070 /*
1071 * Fetch the result. Setting the timeout to 0 here means use
1072 * res_timeout from the configuration.
1073 */
1074 status = fr_ldap_result(&result, NULL, ldap_conn, dir_ctx->msgid, LDAP_MSG_ALL, NULL, fr_time_delta_from_msec(0));
1075 if (status != LDAP_PROC_SUCCESS) {
1076 PERROR("Failed querying for directory type");
1077 if (result) ldap_msgfree(result);
1078 error:
1079 talloc_free(dir_ctx);
1080 if (local) talloc_free(local);
1082 return;
1083 }
1084
1085 fr_ldap_directory_result_parse(ldap_conn->directory, ldap_conn->handle, result, ldap_conn->config->name);
1086 ldap_msgfree(result);
1087
1088 /*
1089 * If the server does not support any of the relevant controls, we just
1090 * tidy up - no point in signalling to reconnect.
1091 */
1092 if (ldap_conn->directory->sync_type == FR_LDAP_SYNC_NONE) {
1093 ERROR("LDAP sync configured for directory which does not support any suitable control");
1094 talloc_free(dir_ctx);
1095 connection_signal_halt(ldap_conn->conn);
1096 return;
1097 }
1098
1099 /*
1100 * We've done all the preparation work on the LDAP connection, now
1101 * use normal network event listeners.
1102 */
1104 fr_network_listen_add(thread->nr, thread->li);
1105
1106 DEBUG2("Starting sync(s)");
1107
1108 local = talloc_new(NULL);
1109
1110 /*
1111 * Sync operations start by sending a fake packet to run
1112 * the load Cookie section in order to retrieve the cookie
1113 */
1114 for (i = 0; i < talloc_array_length(inst->parent->sync_config); i++) {
1115 if (proto_ldap_cookie_load_send(local, inst, i, thread) < 0) goto error;
1116 }
1117
1118 talloc_free(dir_ctx);
1119 talloc_free(local);
1120}
1121
1122/** Allocate a child listener
1123 *
1124 * Called as a watch function when the LDAP connection enters the INIT state
1125 */
1127 UNUSED connection_state_t state, void *uctx)
1128{
1129 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(uctx, proto_ldap_sync_ldap_thread_t);
1130 fr_listen_t *li;
1131
1132 MEM(li = talloc_zero(conn, fr_listen_t));
1133
1134 thread->li = li;
1135 li->thread_instance = thread;
1136
1138 li->name = li->app_io->common.name;
1140
1141 /*
1142 * Use the app from the parent listener to access
1143 * the encoder / decoder functions
1144 */
1145 li->app = thread->parent->app;
1146 li->app_instance = thread->parent->app_instance;
1147 li->server_cs = thread->inst->parent->server_cs;
1148}
1149
1150/** Callback for closure of LDAP connection
1151 *
1152 * Schedules re-start of the connection if appropriate
1153 */
1155 UNUSED connection_state_t state, void *uctx)
1156{
1157 fr_listen_t *listen = talloc_get_type_abort(uctx, fr_listen_t);
1158 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(listen->thread_instance, proto_ldap_sync_ldap_thread_t);
1159 proto_ldap_sync_ldap_t const *inst = thread->inst;
1160
1161 if (fr_event_loop_exiting(thread->el)) return;
1162
1163 if (prev == CONNECTION_STATE_CONNECTED) {
1164 ERROR("LDAP connection closed. Scheduling restart in %pVs",
1165 fr_box_time_delta(inst->handle_config.reconnection_delay));
1166 if (fr_event_timer_in(thread, thread->el, &thread->conn_retry_ev,
1167 inst->handle_config.reconnection_delay,
1168 proto_ldap_connection_init, listen) < 0) {
1169 FATAL("Failed inserting event: %s", fr_strerror());
1170 }
1171 }
1172}
1173
1174/** Query an LDAP server to establish its type
1175 *
1176 * Called as a watch function once the LDAP connection enters the CONNECTED state
1177 *
1178 * There are three different forms of LDAP sync/persistent search - so we need
1179 * to know what we're dealing with, and whether the relevant options have been enabled.
1180 */
1182 UNUSED connection_state_t state, void *uctx)
1183{
1184 proto_ldap_sync_ldap_thread_t *thread = talloc_get_type_abort(uctx, proto_ldap_sync_ldap_thread_t);
1185 fr_listen_t *listen = talloc_get_type_abort(thread->parent, fr_listen_t);
1188 fr_ldap_connection_t *ldap_conn = talloc_get_type_abort(conn->h, fr_ldap_connection_t);
1189
1190 proto_ldap_dir_ctx *dir_ctx;
1191
1192 if (ldap_conn->fd < 0) {
1193 connection_failed:
1194 if (fr_event_timer_in(thread, thread->el, &thread->conn_retry_ev,
1195 inst->handle_config.reconnection_delay,
1196 proto_ldap_connection_init, listen) < 0) {
1197 FATAL("Failed inserting event: %s", fr_strerror());
1198 }
1199 return;
1200 }
1201
1202 thread->li->fd = ldap_conn->fd;
1203
1204 MEM(dir_ctx = talloc_zero(inst, proto_ldap_dir_ctx));
1205 if (!dir_ctx) goto connection_failed;
1206
1207 dir_ctx->main_listen = listen;
1208 dir_ctx->conn = conn;
1209 dir_ctx->child_listen = thread->li;
1210
1211#ifdef SO_RCVBUF
1212 if (inst->recv_buff_is_set) {
1213 int opt;
1214
1215 opt = inst->recv_buff;
1216 if (setsockopt(ldap_conn->fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(int)) < 0) {
1217 WARN("Failed setting 'recv_buff': %s", fr_syserror(errno));
1218 }
1219 }
1220#endif
1221
1222 /*
1223 * Set the callback which will handle the results of this query
1224 */
1225 if (fr_event_fd_insert(conn, NULL, conn->el, ldap_conn->fd,
1227 NULL,
1229 dir_ctx) < 0) {
1230 goto connection_failed;
1231 }
1232
1233 /*
1234 * Allocate the directory structure and send the query
1235 */
1236 dir_ctx->msgid = fr_ldap_conn_directory_alloc_async(ldap_conn);
1237
1238 if (dir_ctx->msgid < 0) {
1239 talloc_free(dir_ctx);
1240 goto connection_failed;
1241 }
1242
1243 /*
1244 * Add a watch to catch closed LDAP connections
1245 */
1247 _proto_ldap_socket_closed, true, listen);
1248}
1249
1250/** Callback triggered when parent listener app_io has its event list set
1251 *
1252 * Initiates the actual outbound LDAP connection
1253 *
1254 * @param[in] li The parent listener.
1255 * @param[in] el Event list for this listener.
1256 * @param[in] nr Network handler.
1257 */
1259{
1262
1263 /*
1264 * Set up thread data
1265 */
1266 thread->name = inst->handle_config.name;
1267 thread->parent = li;
1268 thread->el = el;
1269 thread->nr = nr;
1270 thread->inst = inst;
1271
1272 /*
1273 * Initialise the connection
1274 */
1276}
1277
1278static int mod_instantiate(module_inst_ctx_t const *mctx)
1279{
1280 proto_ldap_sync_ldap_t *inst = talloc_get_type_abort(mctx->mi->data, proto_ldap_sync_ldap_t);
1281 CONF_SECTION *conf = mctx->mi->conf;
1282 char const *server;
1283
1284 /*
1285 * Verify that the LDAP server configuration is valid, either
1286 * distinct server and port or an LDAP url.
1287 */
1288 fr_assert(inst->server);
1289
1290 inst->parent = talloc_get_type_abort(mctx->mi->parent->data, proto_ldap_sync_t);
1291 inst->cs = conf;
1292
1293 if (inst->recv_buff_is_set) {
1294 FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, >=, 32);
1295 FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, <=, INT_MAX);
1296 }
1297
1298 server = inst->server;
1299 inst->handle_config.server = talloc_strdup(inst, "");
1300
1301 if (ldap_is_ldap_url(server)) {
1302 if (fr_ldap_server_url_check(&inst->handle_config, server, conf) < 0) return -1;
1303 } else {
1304 if (fr_ldap_server_config_check(&inst->handle_config, server, conf) < 0) return -1;
1305 }
1306
1307 inst->handle_config.server[talloc_array_length(inst->handle_config.server) - 1] = '\0';
1308
1309 inst->handle_config.name = talloc_typed_asprintf(inst, "proto_ldap_conn (%s)",
1311
1312 return 0;
1313}
1314
1316 .common = {
1317 .magic = MODULE_MAGIC_INIT,
1318 .name = "ldap_sync_child"
1319 },
1323
1324 .default_message_size = 4096,
1325 .track_duplicates = false,
1326};
1327
1329 .common = {
1330 .magic = MODULE_MAGIC_INIT,
1331 .name = "ldap_sync_ldap",
1333 .inst_size = sizeof(proto_ldap_sync_ldap_t),
1334 .thread_inst_size = sizeof(proto_ldap_sync_ldap_thread_t),
1335 .instantiate = mod_instantiate
1336 },
1337
1338 .default_message_size = 4096,
1339 .track_duplicates = false,
1340
1341 .event_list_set = mod_event_list_set,
1342};
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:470
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#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:112
#define unlikely(_x)
Definition build.h:381
#define UNUSED
Definition build.h:315
#define NUM_ELEMENTS(_t)
Definition build.h:337
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:642
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:502
#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:268
#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:282
#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:256
@ CONF_FLAG_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition cf_parse.h:418
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:579
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:684
char const * cf_section_name(CONF_SECTION const *cs)
Return name2 if set, else name1.
Definition cf_util.c:1197
#define cf_parent(_cf)
Definition cf_util.h:101
static int max_outstanding
connection_state_t
Definition connection.h:45
@ CONNECTION_STATE_CLOSED
Connection has been closed.
Definition connection.h:55
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition connection.h:52
@ CONNECTION_STATE_INIT
Init state, sets up connection.
Definition connection.h:49
@ CONNECTION_FAILED
Connection is being reconnected because it failed.
Definition connection.h:84
#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:767
#define fr_dbuff_init(_out, _start, _len_or_end)
Initialise an dbuff for encoding or decoding.
Definition dbuff.h:354
#define fr_dbuff_buff(_dbuff_or_marker)
Return the underlying buffer in a dbuff or one of marker.
Definition dbuff.h:882
#define FR_DBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
Create a function local and thread local extensible dbuff.
Definition dbuff.h:556
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2400
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:268
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:281
Specifies an attribute which must be present for the module to function.
Definition dict.h:267
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:280
#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:486
static void * fr_dlist_pop_head(fr_dlist_head_t *list_head)
Remove the head item in a list.
Definition dlist.h:672
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:378
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:275
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:555
#define fr_event_fd_insert(...)
Definition event.h:232
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:62
#define fr_event_timer_in(...)
Definition event.h:255
#define GLOBAL_LIB_TERMINATOR
Definition global_lib.h:51
Structure to define how to initialise libraries with global configuration.
Definition global_lib.h:38
bool allow_name_only
Allow name only pairs.
Definition internal.h:36
char const * name
printable name for this socket - set by open
Definition listen.h:29
void const * app_instance
Definition listen.h:38
size_t default_message_size
copied from app_io, but may be changed
Definition listen.h:51
fr_app_t const * app
Definition listen.h:37
void const * app_io_instance
I/O path configuration context.
Definition listen.h:32
CONF_SECTION * server_cs
CONF_SECTION of the server.
Definition listen.h:40
void * thread_instance
thread / socket context
Definition listen.h:33
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:31
size_t fr_network_listen_outstanding(fr_network_t *nr, fr_listen_t *li)
Get the number of outstanding packets.
Definition network.c:805
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:763
int fr_network_listen_add(fr_network_t *nr, fr_listen_t *li)
Add a fr_listen_t to a network.
Definition network.c:236
connection_t * fr_ldap_connection_state_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, fr_ldap_config_t const *config, char const *log_prefix)
Alloc a self re-establishing connection to an LDAP server.
Definition connection.c:386
int fr_ldap_conn_directory_alloc_async(fr_ldap_connection_t *ldap_conn)
Async extract useful information from the rootDSE of the LDAP server.
Definition directory.c:287
fr_ldap_sync_type_t sync_type
What kind of LDAP sync this directory supports.
Definition base.h:211
LDAP * handle
libldap handle.
Definition base.h:333
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition base.h:342
int fd
File descriptor for this connection.
Definition base.h:349
void fr_ldap_state_error(fr_ldap_connection_t *c)
Signal that there's been an error on the connection.
Definition state.c:134
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:605
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition base.h:344
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:701
char const * name
Name of the module that created this connection.
Definition base.h:222
fr_time_delta_t reconnection_delay
How long to wait before attempting to reconnect.
Definition base.h:311
int fr_ldap_directory_result_parse(fr_ldap_directory_t *directory, LDAP *handle, LDAPMessage *result, char const *name)
Definition directory.c:52
void * uctx
User data associated with the handle.
Definition base.h:354
@ 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:345
char const ** naming_contexts
Databases served by this directory.
Definition base.h:213
fr_ldap_rcode_t
Codes returned by fr_ldap internal functions.
Definition base.h:582
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition base.h:585
@ LDAP_PROC_BAD_CONN
Transitory error, caller should retry the operation with a new connection.
Definition base.h:589
@ LDAP_PROC_REFRESH_REQUIRED
Don't continue with the current refresh phase, exit, and retry the operation with a NULL cookie.
Definition base.h:604
Tracks the state of a libldap connection handle.
Definition base.h:332
#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:1106
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:228
#define FATAL(_fmt,...)
Definition log.h:224
#define DEBUG3(_fmt,...)
Definition log.h:266
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:643
talloc_free(reap)
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:2755
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:1260
Stores all information relating to an event list.
Definition event.c:411
@ 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
int strncasecmp(char *s1, char *s2, int n)
Definition missing.c:36
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:2319
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:770
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:693
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:183
char const * filter
Filter to retrieve only user objects.
CONF_SECTION * server_cs
server CS for this listener.
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.
fr_event_timer_t const * ev
Event for retrying cookie load.
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.
static void proto_ldap_connection_init(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *user_ctx)
Attempt to (re)initialise a connection.
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 void ldap_sync_retry_event(fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Event to handle sending of any change packets which failed to send.
static fr_dict_attr_t const * attr_ldap_sync_packet_id
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
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
void ldap_sync_cookie_event(fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Event to handle storing of cookies on a timed basis.
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.
int8_t sync_state_cmp(void const *one, void const *two)
Compare two sync state structures on msgid.
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[]
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 void proto_ldap_cookie_load_retry(fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Timer event to retry running "load Cookie" on failures.
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_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.
fr_event_timer_t const * conn_retry_ev
When to retry re-establishing the conn.
@ 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.
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_event_timer_t const * cookie_ev
Timer event for sending cookies.
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_timer_t const * retry_ev
Timer event for retrying failed changes.
fr_event_list_t * el
Network side event list.
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:310
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:303
#define fr_assert(_expr)
Definition rad_assert.h:38
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define WARN(fmt,...)
Definition radclient.h:47
static rs_t * conf
Definition radsniff.c:53
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:577
bool 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:741
The main red black tree structure.
Definition rb.h:73
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_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:535
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:329
void * data
Module's instance data.
Definition module.h:271
module_instance_t const * parent
Parent module's instance (if any).
Definition module.h:337
conf_parser_t const * config
How to convert a CONF_SECTION to a module instance.
Definition module.h:198
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:974
return count
Definition module.c:163
eap_aka_sim_process_conf_t * inst
fr_aka_sim_id_type_t type
fr_pair_t * vp
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
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:772
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:492
#define talloc_get_type_abort_const
Definition talloc.h:282
static fr_time_delta_t fr_time_delta_from_msec(int64_t msec)
Definition time.h:575
"server local" time.
Definition time.h:69
@ T_OP_ADD_EQ
Definition token.h:69
int trigger_exec(unlang_interpret_t *intp, CONF_SECTION const *cs, char const *name, bool rate_limit, fr_pair_list_t *args)
Execute a trigger - call an executable to process an event.
Definition trigger.c:233
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:309
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:94
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:286
#define fr_pair_list_append_by_da_parent_len(_ctx, _vp, _list, _attr, _val, _len, _tainted)
Definition pair.h:331
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:554
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, bool tainted)
Definition value.c:5315
#define fr_box_time_delta(_val)
Definition value.h:343