The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 05e8afe2833cdf4c19cc2f6d32350ba1d1821860 $
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)) {
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_exec(unlang_interpret_get_thread_default(), config->cs, "ldap_sync.start", true, &sync->trigger_args);
151
152 /*
153 * Register event to store cookies at a regular interval
154 */
155 if (fr_event_timer_in(sync, conn->conn->el, &sync->cookie_ev,
156 inst->cookie_interval, 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 if (sync_new_cookie(&new_cookie, sync, ber) == -1) {
496 error:
497 if (sync_uuids) ber_bvarray_free(sync_uuids);
498 if (ber) ber_free(ber, 1);
499 if (data) ber_bvfree(data);
500 if (oid) ldap_memfree(oid);
501
502 return -1;
503 }
504
505 if (!new_cookie) {
506 ERROR("Missing cookie value");
507 goto error;
508 }
509 break;
510
511 /*
512 * During the initial refresh operation, indicates the
513 * end of the "present" phase.
514 */
515 case LDAP_TAG_SYNC_REFRESH_PRESENT:
516 switch (sync->phase) {
517 case SYNC_PHASE_INIT:
519 break;
520
522 break;
523
524 default:
525 ERROR("Invalid refresh phase transition (%s->%s)",
526 fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"),
528 goto error;
529 }
530
531 if (ber_scanf(ber, "{") == LBER_ERROR) {
532 ERROR("Malformed refreshPresent sequence");
533 goto error;
534 }
535
536 if (sync_new_cookie(&new_cookie, sync, ber) == -1) goto error;
537
538 if (ber_peek_tag(ber, &len) == LDAP_TAG_REFRESHDONE) {
539 if (ber_scanf(ber, "b", &refresh_done) == LBER_ERROR) {
540 ERROR("Malformed refresh sequence: Missing refreshDone tag value");
541 goto error;
542 }
543 } else {
544 refresh_done = true; /* Default (when absent) is true */
545 }
546
547 if (ber_scanf(ber, "}") == LBER_ERROR ) {
548 ERROR("Malformed refreshPresent sequence");
549 goto error;
550 }
551
552 if (refresh_done) sync->phase = SYNC_PHASE_DONE;
553 break;
554
555 /*
556 * During the initial refresh operation, indicates the
557 * end of the "delete" phase.
558 */
559 case LDAP_TAG_SYNC_REFRESH_DELETE:
560 switch (sync->phase) {
561 case SYNC_PHASE_INIT:
563 sync->phase = SYNC_PHASE_DELETE;
564 break;
565
567 break;
568
569 default:
570 ERROR("Invalid refresh phase transition (%s->%s)",
571 fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"),
573
574 goto error;
575 }
576
577 if (ber_scanf(ber, "{") == LBER_ERROR) {
578 ERROR("Malformed refreshPresent sequence");
579 goto error;
580 }
581
582 if (sync_new_cookie(&new_cookie, sync, ber) == -1) goto error;
583
584 if (ber_peek_tag(ber, &len) == LDAP_TAG_REFRESHDONE) {
585 if (ber_scanf(ber, "b", &refresh_done) == LBER_ERROR) {
586 ERROR("Malformed refresh sequence: Missing refreshDone tag value");
587 goto error;
588 }
589 } else {
590 refresh_done = true;
591 }
592
593 if (ber_scanf(ber, "}") == LBER_ERROR ) {
594 ERROR("Malformed refreshPresent sequence");
595 goto error;
596 }
597 if (refresh_done) sync->phase = SYNC_PHASE_DONE;
598 break;
599
600 /*
601 * An intermediate response message with the syncInfoValue containing
602 * a syncIDSet.
603 * If refreshDeletes is false, the list of UUIDs are "present"
604 * If refreshDeletes is true, the list of UUIDs are entries to be deleted
605 */
606 case LDAP_TAG_SYNC_ID_SET:
607 if (ber_scanf( ber, "{") == LBER_ERROR) {
608 ERROR("Malformed syncIDSet");
609 goto error;
610 }
611
612 if (sync_new_cookie(&new_cookie, sync, ber) == -1) goto error;
613
614 if (ber_peek_tag(ber, &len) == LDAP_TAG_REFRESHDELETES) {
615 if (ber_scanf(ber, "b", &refresh_deletes) == LBER_ERROR) {
616 ERROR("Malformed refresh_deletes tag");
617 goto error;
618 }
619 }
620
621 if ((ber_scanf(ber, "[W]}", &sync_uuids) == LBER_ERROR) || !sync_uuids) {
622 ERROR("syncIDSet missing set of IDs");
623 goto error;
624 }
625
626 if (refresh_deletes) {
627 /*
628 * refresh_deletes == true indicates we are starting, in or at the end
629 * of a delete phase.
630 */
631 switch (sync->phase) {
632 case SYNC_PHASE_INIT:
634 sync->phase = SYNC_PHASE_DELETE;
635 break;
636
638 break;
639
640 default:
641 ERROR("Invalid refresh phase transition (%s->%s)",
642 fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"),
644 goto error;
645 }
646 } else {
647 /*
648 * refresh_deletes == false indicates we are starting, in or at the end
649 * of a present phase
650 */
651 switch (sync->phase) {
652 case SYNC_PHASE_INIT:
654 break;
655
657 break;
658
659 default:
660 ERROR("Invalid refresh phase transition (%s->%s)",
661 fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"),
663 goto error;
664 }
665 }
666
667 /*
668 * Process the list of UUIDs provided.
669 * The value of refresh_deletes indicates whether these are "present" or "delete"
670 */
671 for (i = 0; sync_uuids[i].bv_val != NULL; i++) {
672 if (sync_uuids[i].bv_len != SYNC_UUID_LENGTH) {
673 ERROR("Invalid entryUUID length, expected " STRINGIFY(SYNC_UUID_LENGTH) " "
674 "bytes got %zu bytes", sync_uuids[i].bv_len);
675 goto error;
676 }
677 ret = ldap_sync_entry_send(sync, (uint8_t const *)sync_uuids[i].bv_val, NULL, NULL,
678 (refresh_deletes ? SYNC_OP_DELETE : SYNC_OP_PRESENT));
679 if (ret < 0) goto error;
680 }
681
682 ber_bvarray_free(sync_uuids);
683 sync_uuids = NULL;
684 break;
685
686 default:
687 ERROR("Invalid syncInfo tag %lu", sync_info_tag);
688 goto error;
689
690 }
691
692 if (new_cookie) {
693 ret = ldap_sync_cookie_store(sync, false);
694 }
695
696 if (ber) ber_free(ber, 1);
697 if (oid) ldap_memfree(oid);
698 if (data) ber_bvfree(data);
699
700 return ret;
701}
702
703/** Handle result code of e-syncRefreshRequired
704 *
705 * If the server wishes to indicate that a refresh is required, it sends a searchResultDone
706 * message with the result code e-syncRefreshRequired result code. Any cookie provided
707 * should be used on a query to re-start the sync. If no cookie is provided, the new
708 * query should be performed without a cookie to get a full refresh.
709 *
710 * @param[in] sync message was associated with.
711 * @param[in] msg requesting the refresh.
712 * @param[in] ctrls associated with the msg.
713 * @return
714 * - 0 on success.
715 * - -1 on failure.
716 */
717int rfc4533_sync_refresh_required(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
718{
719 int refresh_deletes = 0;
720
721 int i;
722 BerElement *ber = NULL;
723 ber_len_t len;
724 bool new_cookie = false;
725
726 fr_assert(sync);
727 fr_assert(sync->conn);
728
729 ldap_msgfree(msg);
730
731 /*
732 * We may or may not have controls.
733 * If the server wants the refresh to occur from a specific change it will
734 * send a cookie which should be used when re-starting the sync query.
735 */
736 if (ctrls) {
737 for (i = 0; ctrls[i] != NULL; i++) {
738 if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_SYNC_DONE) == 0) break;
739 }
740
741 if (ctrls[i]) {
742 ber = ber_init(&ctrls[i]->ldctl_value);
743 if (!ber) {
744 ERROR("Failed allocating ber to handle syncDoneValue control");
745
746 error:
747 if (ber) ber_free(ber, 1);
748 return -1;
749 }
750
751 if (ber_scanf( ber, "{" /*"}"*/) == LBER_ERROR) goto error;
752
753 if (sync_new_cookie(&new_cookie, sync, ber) == -1) goto error;
754
755 if (ber_peek_tag(ber, &len) == LDAP_TAG_REFRESHDELETES) {
756 if (ber_scanf(ber, "b", &refresh_deletes) == LBER_ERROR) {
757 ERROR("Malformed refresh sequence: Missing refreshDeletes tag value");
758 goto error;
759 }
760 }
761
762 if (ber_scanf( ber, /*"{"*/ "}" ) == LBER_ERROR) {
763 ERROR("Malformed syncDoneValue sequence");
764 goto error;
765 }
766
767 ber_free(ber, 1);
768 }
769 }
770
771 /*
772 * If we received e-syncRefreshRequired but no cookie the server is
773 * indicating we should clear the cookie before restarting the sync.
774 */
775 if (!new_cookie) {
776 talloc_free(sync->cookie);
777 sync->cookie = NULL;
778 new_cookie = true;
779 }
780
781 return ldap_sync_cookie_store(sync, true);
782}
log_entry msg
Definition acutest.h:794
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define STRINGIFY(x)
Definition build.h:197
#define UNUSED
Definition build.h:315
#define NUM_ELEMENTS(_t)
Definition build.h:337
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:139
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define fr_event_timer_in(...)
Definition event.h:255
unlang_interpret_t * unlang_interpret_get_thread_default(void)
Get the default interpreter for this thread.
Definition interpret.c:1787
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:650
LDAP * handle
libldap handle.
Definition base.h:333
uint8_t * fr_ldap_berval_to_bin(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced buffer.
Definition util.c:409
void * uctx
User data associated with the handle.
Definition base.h:354
connection_t * conn
Connection state handle.
Definition base.h:345
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition base.h:585
Tracks the state of a libldap connection handle.
Definition base.h:332
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:228
#define DEBUG3(_fmt,...)
Definition log.h:266
#define HEXDUMP2(_data, _len, _fmt,...)
Definition log.h:722
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition log.h:259
talloc_free(reap)
unsigned char uint8_t
static const conf_parser_t config[]
Definition base.c:183
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.
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.
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.
int8_t sync_state_cmp(void const *one, void const *two)
Compare two sync state structures on msgid.
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.
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.
#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_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.
State of an individual sync.
#define fr_assert(_expr)
Definition rad_assert.h:38
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define WARN(fmt,...)
Definition radclient.h:47
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:626
#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:246
The main red black tree structure.
Definition rb.h:73
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:717
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:772
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
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_slen_t data
Definition value.h:1265
#define fr_box_strvalue_len(_val, _len)
Definition value.h:286