The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
rfc4533.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: 1357266db10e31ce626d055a3439922e8703c523 $
19 * @file rfc4533.c
20 * @brief LDAP sync callback functions for RFC 4533 servers.
21 *
22 * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
23 */
24
25#define LOG_PREFIX "ldap_sync_rfc4533"
26
27#include "rfc4533.h"
28#include <freeradius-devel/util/debug.h>
29
30/** Types of Sync Info messages
31 */
33 { L("newCookie"), LDAP_TAG_SYNC_NEW_COOKIE },
34 { L("refreshDelete"), LDAP_TAG_SYNC_REFRESH_DELETE },
35 { L("refreshIDSet"), LDAP_TAG_SYNC_ID_SET },
36 { L("refreshPresent"), LDAP_TAG_SYNC_REFRESH_PRESENT }
37};
39
40/** Phases of an RFC 4533 sync
41 */
43 { L("delete"), SYNC_PHASE_DELETE },
44 { L("done"), SYNC_PHASE_DONE },
45 { L("init"), SYNC_PHASE_INIT },
46 { L("present"), SYNC_PHASE_PRESENT },
47};
49
50/** Allocate and initialise RFC 4533 sync queries.
51 *
52 * The Sync Request Control is an LDAP Control [RFC4511] where the controlType is the object
53 * identifier 1.3.6.1.4.1.4203.1.9.1.1 and the controlValue, an OCTET STRING, contains a
54 * BER-encoded syncRequestValue.
55 *
56 * syncRequestValue ::= SEQUENCE {
57 * mode ENUMERATED {
58 * -- 0 unused
59 * refreshOnly (1),
60 * -- 2 reserved
61 * refreshAndPersist (3)
62 * },
63 * cookie syncCookie OPTIONAL,
64 * reloadHint BOOLEAN DEFAULT FALSE
65 * }
66 *
67 * reloadHint specifies whether we prefer a complete directory load or an eSyncRefreshRequired
68 * response when the provided cookie does not give the server a point in its change log
69 * from which it can send suitable changes to bring the client into sync.
70 * We always send 'false' since we handle eSyncRefreshRequired.
71 *
72 * The Sync Request Control is only applicable to the SearchRequest Message.
73 */
74int rfc4533_sync_init(fr_ldap_connection_t *conn, size_t sync_no, proto_ldap_sync_t const *inst, uint8_t const *cookie)
75{
76 LDAPControl ctrl = {0}, *ctrls[2] = { &ctrl, NULL };
77 BerElement *ber = NULL;
78 static char const *sync_ctl_oid = LDAP_CONTROL_SYNC;
79 int ret;
80 fr_rb_tree_t *tree;
81 sync_state_t *sync;
82 sync_config_t const *config = inst->sync_config[sync_no];
83
84 fr_assert(conn);
86
87 if (!conn->uctx) {
88 MEM(tree = fr_rb_inline_talloc_alloc(conn, sync_state_t, node, sync_state_cmp, NULL));
89 conn->uctx = tree;
90 } else {
91 tree = talloc_get_type_abort(conn->uctx, fr_rb_tree_t);
92 }
93
94 ber = ber_alloc_t(LBER_USE_DER);
95 if (!ber) {
96 ERROR("Failed allocating ber for sync control");
97 return -1;
98 }
99
100 sync = sync_state_alloc(tree, conn, inst, sync_no, config);
101
102 /*
103 * Might not necessarily have a cookie
104 */
105 if (cookie) {
106 char *bv_val;
107 struct berval bvc;
108
109 HEXDUMP2(cookie, talloc_array_length(cookie), "Sync starting with cookie: ");
110 memcpy(&bv_val, &cookie, sizeof(bv_val));
111
112 bvc.bv_val = bv_val;
113 bvc.bv_len = talloc_array_length(cookie);
114
115 ber_printf(ber, "{eOb}", LDAP_SYNC_REFRESH_AND_PERSIST, &bvc, false);
116 } else {
117 DEBUG2("Sync starting without a cookie");
118 ber_printf(ber, "{eb}", LDAP_SYNC_REFRESH_AND_PERSIST, false);
119 }
120
121 ret = ber_flatten2(ber, &ctrls[0]->ldctl_value, 0);
122 if (ret < 0) {
123 ERROR("Failed creating sync control");
124 ber_free(ber, 1);
125 error:
126 talloc_free(sync);
127 return -1;
128 }
129
130 memcpy(&ctrls[0]->ldctl_oid, &sync_ctl_oid, sizeof(ctrls[0]->ldctl_oid));
131 ctrl.ldctl_iscritical = 1;
132
133 ret = fr_ldap_search_async(&sync->msgid, NULL, conn, config->base_dn, config->scope,
134 config->filter, config->attrs, ctrls, NULL);
135 ber_free(ber, 1);
136
137 if (ret != LDAP_PROC_SUCCESS) {
138 ERROR("Failed to start RFC 4533 query");
139 goto error;
140 }
141
142 if (fr_rb_insert(tree, sync) != 0) {
143 ERROR("Duplicate sync (msgid %i)", sync->msgid);
144 goto error;
145 }
146
147 DEBUG3("Sync created with base dn \"%s\", filter \"%s\", msgid %i",
148 sync->config->base_dn, sync->config->filter, sync->msgid);
149
150 trigger(unlang_interpret_get_thread_default(), config->cs, NULL, "modules.ldap_sync.start", true, &sync->trigger_args, inst);
151
152 /*
153 * Register event to store cookies at a regular interval
154 */
155 if (fr_timer_in(sync, conn->conn->el->tl, &sync->cookie_ev,
156 inst->cookie_interval, false, ldap_sync_cookie_event, sync) < 0) {
157 PERROR("Inserting LDAP cookie timer failed");
158 goto error;
159 }
160
161 return 0;
162}
163
164/** Check for the presence of a cookie in a ber value
165 *
166 * If a new cookie is found, the sync state will be updated.
167 *
168 * @param[out] new_cookie Whether we got a new cookie value.
169 * @param[in] sync which the message was associated with.
170 * @param[in] ber value possibly containing a cookie tag (will be advanced).
171 * @return
172 * - 0 success, a cookie was parsed successfully.
173 * - -1 parse error.
174 * - -2 same as existing cookie.
175 */
176static int sync_new_cookie(bool *new_cookie, sync_state_t *sync, BerElement *ber)
177{
178 struct berval cookie;
179 size_t cookie_len;
180 ber_tag_t bv_ret;
181 ber_len_t len;
182
183 if (new_cookie) *new_cookie = false;
184
185 /*
186 * Look for the (optional) cookie.
187 */
188 bv_ret = ber_peek_tag(ber, &len);
189 if ((bv_ret != LDAP_TAG_SYNC_COOKIE) && (bv_ret != LDAP_TAG_SYNC_NEW_COOKIE)) return 0;
190
191 bv_ret = ber_scanf(ber, "m", &cookie);
192 if (bv_ret == LBER_ERROR) {
193 ERROR("Malformed cookie tag");
194 return -1;
195 }
196
197 /*
198 * "no cookie" can mean either no cookie element,
199 * or a NULL cookie element (as per the RFC).
200 */
201 if ((!cookie.bv_val) || (cookie.bv_len == 0)) return 0;
202
203 if (sync->cookie) {
204 if (talloc_array_length(sync->cookie) == cookie.bv_len) {
205 cookie_len = talloc_array_length(sync->cookie);
206 if (memcmp(sync->cookie, cookie.bv_val, cookie.bv_len) == 0) {
207 WARN("Ignoring new cookie \"%pV\": Identical to old cookie",
208 fr_box_strvalue_len((char const *)sync->cookie, cookie_len));
209 return -2;
210 }
211 }
212 }
213
214 talloc_free(sync->cookie);
215 sync->cookie = fr_ldap_berval_to_bin(sync, &cookie);
216 cookie_len = talloc_array_length(sync->cookie);
217 DEBUG3("Got new cookie value \"%pV\" (%zu)",
218 fr_box_strvalue_len((char const *)sync->cookie, cookie_len), cookie_len);
219
220 if (new_cookie) *new_cookie = true;
221
222 return 0;
223}
224
225/** Handle a SearchResultEntry or SearchResultReference response from an RFC 4533 server
226 *
227 * Upon receipt of a search request containing the syncControl the server provides the initial
228 * content using zero or more SearchResultEntries followed by a SearchResultdone.
229 *
230 * Each SearchResultEntry includes a Sync State control with state set to add, an entryUUID
231 * containing the entry's UUID, and no cookie.
232 *
233 * For refreshAndPersist operations SearchResultEntries are also used after the refresh phase
234 * to inform clients of changes to entries within the scope of the search request.
235 *
236 * The Sync State Control is an LDAP Control where the controlType is the object identifier
237 * 1.3.6.1.4.1.4203.1.9.1.2 and the controlValue, an OCTET STRING.
238 * It contains a BER-encoded syncStateValue.
239 *
240 * syncStateValue ::= SEQUENCE {
241 * state ENUMERATED {
242 * present (0),
243 * add (1),
244 * modify (2),
245 * delete (3)
246 * },
247 * entryUUID syncUUID,
248 * cookie syncCookie OPTIONAL
249 * }
250 *
251 * The Sync State Control is only included in SearchResultEntry and SearchResultReference Messages.
252 *
253 * @param[in] sync message was associated with.
254 * @param[in] msg containing an entry to process.
255 * @param[in] ctrls associated with the msg.
256 * @return
257 * - 0 on success.
258 * - -1 on failure.
259 */
260int rfc4533_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
261{
262 int ret = 0, i;
263 ber_tag_t bv_ret;
264 BerElement *ber = NULL;
265 struct berval entry_uuid = { 0 };
267 bool new_cookie;
268
269 fr_assert(sync);
270 fr_assert(sync->conn);
271 fr_assert(msg);
272
273 if (!ctrls) {
274 missing_control:
275 ERROR("searchResEntry missing syncStateValue control");
276 ldap_msgfree(msg);
277 return -1;
278 }
279
280 /*
281 * Every SearchResultEntry/Reference must contain a Sync State Control
282 * describing the state of an object/the changes that should
283 * be made to it.
284 *
285 * If the entry doesn't have this control then the LDAP server
286 * is broken.
287 */
288 for (i = 0; ctrls[i] != NULL; i++) {
289 if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_SYNC_STATE) == 0) break;
290 }
291 if (!ctrls[i]) goto missing_control;
292
293 /*
294 * Get the value of the control.
295 */
296 ber = ber_init(&ctrls[i]->ldctl_value);
297 if (!ber) {
298 ERROR("Failed allocating ber to handle syncStateValue control");
299
300 error:
301 if (ber) ber_free(ber, 1);
302 ldap_msgfree(msg);
303 return -1;
304 }
305
306 bv_ret = ber_scanf(ber, "{em", &op, &entry_uuid);
307 if ((bv_ret == LBER_ERROR) || (entry_uuid.bv_len == 0)) {
308 ERROR("Malformed syncUUID value");
309 goto error;
310 }
311
312 if (sync_new_cookie(&new_cookie, sync, ber) == -1) goto error;
313
314 if (ber_scanf(ber, "}") == LBER_ERROR ) {
315 ERROR("Malformed syncStatevalue sequence");
316 goto error;
317 }
318
319 switch (op) {
320 case SYNC_OP_PRESENT:
321 switch (sync->phase) {
322
323 case SYNC_PHASE_INIT:
325 break;
326
328 break;
329
330 default:
331 bad_phase:
332 ERROR("Entries with %s state are not allowed during refresh %s phase",
333 fr_table_str_by_value(sync_op_table, op, "<unknown>"),
334 fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"));
335 goto error;
336 }
337 break;
338
339 case SYNC_OP_DELETE:
340 switch (sync->phase) {
342 case SYNC_PHASE_DONE:
343 break;
344
345 default:
346 goto bad_phase;
347 }
348 break;
349
350 case SYNC_OP_ADD:
351 case SYNC_OP_MODIFY:
352 /*
353 * RFC 4533 is less than clear about when added or modified entries
354 * should be sent during the initial refresh stage.
355 * If this is the first thing we see, then it will be the Present
356 * phase.
357 * All other phases can receive added / modified entries, and those
358 * do not indicate a change in phase.
359 */
360 switch (sync->phase) {
361 case SYNC_PHASE_INIT:
363 break;
364
365 default:
366 break;
367 }
368 break;
369
370
371 default:
372 ERROR("Unknown entry state (%i)", op);
373 goto error;
374 }
375
376 if (DEBUG_ENABLED3) {
377 char *entry_dn;
378 fr_value_box_t uuid_box;
379
380 entry_dn = ldap_get_dn(sync->conn->handle, msg);
381 fr_ldap_berval_to_value_shallow(&uuid_box, &entry_uuid);
382
383 DEBUG3("Processing %s (%s), dn \"%s\", entryUUID %pV",
384 fr_table_str_by_value(sync_ldap_msg_table, ldap_msgtype(msg), "<unknown>"),
385 fr_table_str_by_value(sync_op_table, op, "<unknown>"),
386 entry_dn ? entry_dn : "<unknown>",
387 &uuid_box);
388
389 ldap_memfree(entry_dn);
390 }
391
392 /*
393 * Send the appropriate packet type to process the message
394 */
395 switch (ldap_msgtype(msg)) {
396 case LDAP_RES_SEARCH_ENTRY:
397 ret = ldap_sync_entry_send(sync, (uint8_t const *)entry_uuid.bv_val, NULL, msg, op);
398 break;
399
400 case LDAP_RES_SEARCH_REFERENCE:
401 /* TODO - handle references */
402 ldap_msgfree(msg);
403 break;
404
405 default:
406 fr_assert(0);
407 }
408
409 /*
410 * We have a new cookie - store it
411 */
412 if ((ret == 0) && new_cookie) {
413 ret = ldap_sync_cookie_store(sync, false);
414 }
415 ber_free(ber, 1);
416
417 return ret;
418}
419
420/** Handle a LDAP_RES_INTERMEDIATE (SyncInfo) response
421 *
422 * These allow the LDAP server to communicate sync state to clients
423 *
424 * The Sync Info Message is an LDAP Intermediate Response Message [RFC4511] where
425 * responseName is the object identifier 1.3.6.1.4.1.4203.1.9.1.4 and responseValue
426 * contains a BER-encoded syncInfoValue.
427 *
428 * syncInfoValue ::= CHOICE {
429 * newcookie [0] syncCookie,
430 * refreshDelete [1] SEQUENCE {
431 * cookie syncCookie OPTIONAL,
432 * refreshDone BOOLEAN DEFAULT TRUE
433 * },
434 * refreshPresent [2] SEQUENCE {
435 * cookie syncCookie OPTIONAL,
436 * refreshDone BOOLEAN DEFAULT TRUE
437 * },
438 * syncIdSet [3] SEQUENCE {
439 * cookie syncCookie OPTIONAL,
440 * refreshDeletes BOOLEAN DEFAULT FALSE,
441 * syncUUIDs SET OF syncUUID
442 * }
443 * }
444 *
445 * @param[in] sync message was associated with.
446 * @param[in] msg containing an entry to process.
447 * @param[in] ctrls associated with the msg.
448 * @return
449 * - 0 on success.
450 * - -1 on failure.
451 */
452int rfc4533_sync_intermediate(sync_state_t *sync, LDAPMessage *msg, UNUSED LDAPControl **ctrls)
453{
454 int ret, i;
455 char *oid = NULL;
456 struct berval *data = NULL;
457 BerElement *ber = NULL;
458 ber_len_t len;
459 ber_tag_t sync_info_tag;
460 int refresh_deletes = 0;
461 BerVarray sync_uuids = NULL;
462
463 bool new_cookie;
464 int refresh_done = false;
465
466 /*
467 * Extract data from the message. Setting freeit to 1 means the message
468 * will be freed after the extract.
469 */
470 ret = ldap_parse_intermediate(sync->conn->handle, msg, &oid, &data, NULL, 1);
471 if (!fr_cond_assert(ret == LDAP_SUCCESS)) return -1;
472
473 if (!oid || (strcmp(oid, LDAP_SYNC_INFO) != 0)) {
474 WARN("Ignoring intermediateResult with unexpected OID \"%s\"", oid ? oid : "<unknown>");
475 return 0;
476 }
477
478 ber = ber_init(data);
479 if (ber == NULL) {
480 ERROR("Failed allocating ber to handle syncInfo data");
481
482 return -1;
483 }
484
485 sync_info_tag = ber_peek_tag(ber, &len);
486 DEBUG3("Processing syncInfo (%s)", fr_table_str_by_value(sync_info_tag_table, sync_info_tag, "<unknown>"));
487
488 switch (sync_info_tag) {
489 /*
490 * A new cookie to store.
491 * Typically provided when data in the directory has changed
492 * but those changes don't match the search.
493 */
494 case LDAP_TAG_SYNC_NEW_COOKIE:
495 ret = sync_new_cookie(&new_cookie, sync, ber);
496 if (ret == -2) break; /* it's an identical cookie, not an error */
497 if (ret < 0) {
498 error:
499 if (sync_uuids) ber_bvarray_free(sync_uuids);
500 if (ber) ber_free(ber, 1);
501 if (data) ber_bvfree(data);
502 if (oid) ldap_memfree(oid);
503
504 return -1;
505 }
506
507 if (!new_cookie) {
508 ERROR("Missing cookie value");
509 goto error;
510 }
511 break;
512
513 /*
514 * During the initial refresh operation, indicates the
515 * end of the "present" phase.
516 */
517 case LDAP_TAG_SYNC_REFRESH_PRESENT:
518 switch (sync->phase) {
519 case SYNC_PHASE_INIT:
521 break;
522
524 break;
525
526 default:
527 ERROR("Invalid refresh phase transition (%s->%s)",
528 fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"),
530 goto error;
531 }
532
533 if (ber_scanf(ber, "{") == LBER_ERROR) {
534 ERROR("Malformed refreshPresent sequence");
535 goto error;
536 }
537
538 if (sync_new_cookie(&new_cookie, sync, ber) == -1) goto error;
539
540 if (ber_peek_tag(ber, &len) == LDAP_TAG_REFRESHDONE) {
541 if (ber_scanf(ber, "b", &refresh_done) == LBER_ERROR) {
542 ERROR("Malformed refresh sequence: Missing refreshDone tag value");
543 goto error;
544 }
545 } else {
546 refresh_done = true; /* Default (when absent) is true */
547 }
548
549 if (ber_scanf(ber, "}") == LBER_ERROR ) {
550 ERROR("Malformed refreshPresent sequence");
551 goto error;
552 }
553
554 if (refresh_done) sync->phase = SYNC_PHASE_DONE;
555 break;
556
557 /*
558 * During the initial refresh operation, indicates the
559 * end of the "delete" phase.
560 */
561 case LDAP_TAG_SYNC_REFRESH_DELETE:
562 switch (sync->phase) {
563 case SYNC_PHASE_INIT:
565 sync->phase = SYNC_PHASE_DELETE;
566 break;
567
569 break;
570
571 default:
572 ERROR("Invalid refresh phase transition (%s->%s)",
573 fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"),
575
576 goto error;
577 }
578
579 if (ber_scanf(ber, "{") == LBER_ERROR) {
580 ERROR("Malformed refreshPresent sequence");
581 goto error;
582 }
583
584 if (sync_new_cookie(&new_cookie, sync, ber) == -1) goto error;
585
586 if (ber_peek_tag(ber, &len) == LDAP_TAG_REFRESHDONE) {
587 if (ber_scanf(ber, "b", &refresh_done) == LBER_ERROR) {
588 ERROR("Malformed refresh sequence: Missing refreshDone tag value");
589 goto error;
590 }
591 } else {
592 refresh_done = true;
593 }
594
595 if (ber_scanf(ber, "}") == LBER_ERROR ) {
596 ERROR("Malformed refreshPresent sequence");
597 goto error;
598 }
599 if (refresh_done) sync->phase = SYNC_PHASE_DONE;
600 break;
601
602 /*
603 * An intermediate response message with the syncInfoValue containing
604 * a syncIDSet.
605 * If refreshDeletes is false, the list of UUIDs are "present"
606 * If refreshDeletes is true, the list of UUIDs are entries to be deleted
607 */
608 case LDAP_TAG_SYNC_ID_SET:
609 if (ber_scanf( ber, "{") == LBER_ERROR) {
610 ERROR("Malformed syncIDSet");
611 goto error;
612 }
613
614 if (sync_new_cookie(&new_cookie, sync, ber) == -1) goto error;
615
616 if (ber_peek_tag(ber, &len) == LDAP_TAG_REFRESHDELETES) {
617 if (ber_scanf(ber, "b", &refresh_deletes) == LBER_ERROR) {
618 ERROR("Malformed refresh_deletes tag");
619 goto error;
620 }
621 }
622
623 if ((ber_scanf(ber, "[W]}", &sync_uuids) == LBER_ERROR) || !sync_uuids) {
624 ERROR("syncIDSet missing set of IDs");
625 goto error;
626 }
627
628 if (refresh_deletes) {
629 /*
630 * refresh_deletes == true indicates we are starting, in or at the end
631 * of a delete phase.
632 */
633 switch (sync->phase) {
634 case SYNC_PHASE_INIT:
636 sync->phase = SYNC_PHASE_DELETE;
637 break;
638
640 break;
641
642 default:
643 ERROR("Invalid refresh phase transition (%s->%s)",
644 fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"),
646 goto error;
647 }
648 } else {
649 /*
650 * refresh_deletes == false indicates we are starting, in or at the end
651 * of a present phase
652 */
653 switch (sync->phase) {
654 case SYNC_PHASE_INIT:
656 break;
657
659 break;
660
661 default:
662 ERROR("Invalid refresh phase transition (%s->%s)",
663 fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"),
665 goto error;
666 }
667 }
668
669 /*
670 * Process the list of UUIDs provided.
671 * The value of refresh_deletes indicates whether these are "present" or "delete"
672 */
673 for (i = 0; sync_uuids[i].bv_val != NULL; i++) {
674 if (sync_uuids[i].bv_len != SYNC_UUID_LENGTH) {
675 ERROR("Invalid entryUUID length, expected " STRINGIFY(SYNC_UUID_LENGTH) " "
676 "bytes got %zu bytes", sync_uuids[i].bv_len);
677 goto error;
678 }
679 ret = ldap_sync_entry_send(sync, (uint8_t const *)sync_uuids[i].bv_val, NULL, NULL,
680 (refresh_deletes ? SYNC_OP_DELETE : SYNC_OP_PRESENT));
681 if (ret < 0) goto error;
682 }
683
684 ber_bvarray_free(sync_uuids);
685 sync_uuids = NULL;
686 break;
687
688 default:
689 ERROR("Invalid syncInfo tag %lu", sync_info_tag);
690 goto error;
691
692 }
693
694 if (new_cookie) {
695 ret = ldap_sync_cookie_store(sync, false);
696 }
697
698 if (ber) ber_free(ber, 1);
699 if (oid) ldap_memfree(oid);
700 if (data) ber_bvfree(data);
701
702 return ret;
703}
704
705/** Handle result code of e-syncRefreshRequired
706 *
707 * If the server wishes to indicate that a refresh is required, it sends a searchResultDone
708 * message with the result code e-syncRefreshRequired result code. Any cookie provided
709 * should be used on a query to re-start the sync. If no cookie is provided, the new
710 * query should be performed without a cookie to get a full refresh.
711 *
712 * @param[in] sync message was associated with.
713 * @param[in] msg requesting the refresh.
714 * @param[in] ctrls associated with the msg.
715 * @return
716 * - 0 on success.
717 * - -1 on failure.
718 */
719int rfc4533_sync_refresh_required(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
720{
721 int refresh_deletes = 0;
722
723 int i;
724 BerElement *ber = NULL;
725 ber_len_t len;
726 bool new_cookie = false;
727
728 fr_assert(sync);
729 fr_assert(sync->conn);
730
731 ldap_msgfree(msg);
732
733 /*
734 * We may or may not have controls.
735 * If the server wants the refresh to occur from a specific change it will
736 * send a cookie which should be used when re-starting the sync query.
737 */
738 if (ctrls) {
739 for (i = 0; ctrls[i] != NULL; i++) {
740 if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_SYNC_DONE) == 0) break;
741 }
742
743 if (ctrls[i]) {
744 ber = ber_init(&ctrls[i]->ldctl_value);
745 if (!ber) {
746 ERROR("Failed allocating ber to handle syncDoneValue control");
747
748 error:
749 if (ber) ber_free(ber, 1);
750 return -1;
751 }
752
753 if (ber_scanf( ber, "{" /*"}"*/) == LBER_ERROR) goto error;
754
755 if (sync_new_cookie(&new_cookie, sync, ber) == -1) goto error;
756
757 if (ber_peek_tag(ber, &len) == LDAP_TAG_REFRESHDELETES) {
758 if (ber_scanf(ber, "b", &refresh_deletes) == LBER_ERROR) {
759 ERROR("Malformed refresh sequence: Missing refreshDeletes tag value");
760 goto error;
761 }
762 }
763
764 if (ber_scanf( ber, /*"{"*/ "}" ) == LBER_ERROR) {
765 ERROR("Malformed syncDoneValue sequence");
766 goto error;
767 }
768
769 ber_free(ber, 1);
770 }
771 }
772
773 /*
774 * If we received e-syncRefreshRequired but no cookie the server is
775 * indicating we should clear the cookie before restarting the sync.
776 */
777 if (!new_cookie) {
778 talloc_free(sync->cookie);
779 sync->cookie = NULL;
780 new_cookie = true;
781 }
782
783 return ldap_sync_cookie_store(sync, true);
784}
log_entry msg
Definition acutest.h:794
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define STRINGIFY(x)
Definition build.h:216
#define UNUSED
Definition build.h:384
#define NUM_ELEMENTS(_t)
Definition build.h:406
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:177
#define MEM(x)
Definition debug.h:38
#define ERROR(fmt,...)
Definition dhcpclient.c:40
talloc_free(hp)
unlang_interpret_t * unlang_interpret_get_thread_default(void)
Get the default interpreter for this thread.
Definition interpret.c:2561
static void fr_ldap_berval_to_value_shallow(fr_value_box_t *value, struct berval *berval)
Inline function to copy pointers from a berval to a valuebox.
Definition base.h:652
LDAP * handle
libldap handle.
Definition base.h:342
uint8_t * fr_ldap_berval_to_bin(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced buffer.
Definition util.c:764
void * uctx
User data associated with the handle.
Definition base.h:363
connection_t * conn
Connection state handle.
Definition base.h:354
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition base.h:587
Tracks the state of a libldap connection handle.
Definition base.h:341
fr_ldap_rcode_t fr_ldap_search_async(int *msgid, request_t *request, fr_ldap_connection_t *pconn, char const *dn, int scope, char const *filter, char const *const *attrs, LDAPControl **serverctrls, LDAPControl **clientctrls)
Search for something in the LDAP directory.
Definition base.c:529
#define PERROR(_fmt,...)
Definition log.h:233
#define DEBUG3(_fmt,...)
Definition log.h:271
#define HEXDUMP2(_data, _len, _fmt,...)
Definition log.h:739
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition log.h:264
unsigned char uint8_t
static const conf_parser_t config[]
Definition base.c:162
char const * filter
Filter to retrieve only user objects.
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_INVALID
Invalid sync operation.
@ 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.
An instance of a proto_ldap_sync listen section.
Areas of the directory to receive notifications for.
int ldap_sync_cookie_store(sync_state_t *sync, bool refresh)
Add a new cookie packet ctx to the pending list.
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.
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.
fr_table_num_sorted_t const sync_op_table[]
Operations performed on entries.
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_cmp_ret_t sync_state_cmp(void const *one, void const *two)
Compare two sync state structures on msgid.
uint8_t * cookie
Opaque cookie, used to resume synchronisation.
sync_phases_t phase
Phase this sync is in.
int msgid
The unique identifier for this sync session.
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.
fr_timer_t * cookie_ev
Timer event for sending cookies.
#define SYNC_UUID_LENGTH
@ SYNC_PHASE_DELETE
Currently in the delete phase.
@ SYNC_PHASE_DONE
Refresh phase is complete.
@ SYNC_PHASE_INIT
We haven't entered any of the refresh phases.
@ SYNC_PHASE_PRESENT
Currently in the present phase.
fr_ldap_connection_t * conn
Connection the sync is running on.
fr_pair_list_t trigger_args
Arguments to make available in triggers.
State of an individual sync.
#define fr_assert(_expr)
Definition rad_assert.h:37
#define DEBUG2(fmt,...)
#define WARN(fmt,...)
int fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:637
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition rb.h:244
The main red black tree structure.
Definition rb.h:71
static size_t const sync_info_tag_table_len
Definition rfc4533.c:38
int rfc4533_sync_refresh_required(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
Handle result code of e-syncRefreshRequired.
Definition rfc4533.c:719
static size_t const sync_phase_table_len
Definition rfc4533.c:48
static fr_table_num_sorted_t const sync_info_tag_table[]
Types of Sync Info messages.
Definition rfc4533.c:32
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
static int sync_new_cookie(bool *new_cookie, sync_state_t *sync, BerElement *ber)
Check for the presence of a cookie in a ber value.
Definition rfc4533.c:176
static fr_table_num_sorted_t const sync_phase_table[]
Phases of an RFC 4533 sync.
Definition rfc4533.c:42
int rfc4533_sync_intermediate(sync_state_t *sync, LDAPMessage *msg, UNUSED LDAPControl **ctrls)
Handle a LDAP_RES_INTERMEDIATE (SyncInfo) response.
Definition rfc4533.c:452
eap_aka_sim_process_conf_t * inst
#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
#define fr_timer_in(...)
Definition timer.h:87
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_slen_t data
Definition value.h:1367
#define fr_box_strvalue_len(_val, _len)
Definition value.h:334