The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 032bde3334a67a132cf4f4cebb05130c3f06841b $
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  */
74 int 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  */
175 static int sync_new_cookie(bool *new_cookie, sync_state_t *sync, BerElement *ber)
176 {
177  struct berval cookie;
178  size_t cookie_len;
179  ber_tag_t bv_ret;
180  ber_len_t len;
181 
182  if (new_cookie) *new_cookie = false;
183 
184  /*
185  * Look for the (optional) cookie.
186  */
187  bv_ret = ber_peek_tag(ber, &len);
188  if ((bv_ret != LDAP_TAG_SYNC_COOKIE) && (bv_ret != LDAP_TAG_SYNC_NEW_COOKIE)) return 0;
189 
190  bv_ret = ber_scanf(ber, "m", &cookie);
191  if (bv_ret == LBER_ERROR) {
192  ERROR("Malformed cookie tag");
193  return -1;
194  }
195 
196  /*
197  * "no cookie" can mean either no cookie element,
198  * or a NULL cookie element (as per the RFC).
199  */
200  if ((!cookie.bv_val) || (cookie.bv_len == 0)) return 0;
201 
202  if (sync->cookie) {
203  if (talloc_array_length(sync->cookie) == cookie.bv_len) {
204  cookie_len = talloc_array_length(sync->cookie);
205  if (memcmp(sync->cookie, cookie.bv_val, cookie.bv_len) == 0) {
206  WARN("Ignoring new cookie \"%pV\": Identical to old cookie",
207  fr_box_strvalue_len((char const *)sync->cookie, cookie_len));
208  return 0;
209  }
210  }
211  }
212 
213  talloc_free(sync->cookie);
214  sync->cookie = fr_ldap_berval_to_bin(sync, &cookie);
215  cookie_len = talloc_array_length(sync->cookie);
216  DEBUG3("Got new cookie value \"%pV\" (%zu)",
217  fr_box_strvalue_len((char const *)sync->cookie, cookie_len), cookie_len);
218 
219  if (new_cookie) *new_cookie = true;
220 
221  return 0;
222 }
223 
224 /** Handle a SearchResultEntry or SearchResultReference response from an RFC 4533 server
225  *
226  * Upon receipt of a search request containing the syncControl the server provides the initial
227  * content using zero or more SearchResultEntries followed by a SearchResultdone.
228  *
229  * Each SearchResultEntry includes a Sync State control with state set to add, an entryUUID
230  * containing the entry's UUID, and no cookie.
231  *
232  * For refreshAndPersist operations SearchResultEntries are also used after the refresh phase
233  * to inform clients of changes to entries within the scope of the search request.
234  *
235  * The Sync State Control is an LDAP Control where the controlType is the object identifier
236  * 1.3.6.1.4.1.4203.1.9.1.2 and the controlValue, an OCTET STRING.
237  * It contains a BER-encoded syncStateValue.
238  *
239  * syncStateValue ::= SEQUENCE {
240  * state ENUMERATED {
241  * present (0),
242  * add (1),
243  * modify (2),
244  * delete (3)
245  * },
246  * entryUUID syncUUID,
247  * cookie syncCookie OPTIONAL
248  * }
249  *
250  * The Sync State Control is only included in SearchResultEntry and SearchResultReference Messages.
251  *
252  * @param[in] sync message was associated with.
253  * @param[in] msg containing an entry to process.
254  * @param[in] ctrls associated with the msg.
255  * @return
256  * - 0 on success.
257  * - -1 on failure.
258  */
259 int rfc4533_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
260 {
261  int ret = 0, i;
262  ber_tag_t bv_ret;
263  BerElement *ber = NULL;
264  struct berval entry_uuid = { 0 };
266  bool new_cookie;
267 
268  fr_assert(sync);
269  fr_assert(sync->conn);
270  fr_assert(msg);
271 
272  if (!ctrls) {
273  missing_control:
274  ERROR("searchResEntry missing syncStateValue control");
275  ldap_msgfree(msg);
276  return -1;
277  }
278 
279  /*
280  * Every SearchResultEntry/Reference must contain a Sync State Control
281  * describing the state of an object/the changes that should
282  * be made to it.
283  *
284  * If the entry doesn't have this control then the LDAP server
285  * is broken.
286  */
287  for (i = 0; ctrls[i] != NULL; i++) {
288  if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_SYNC_STATE) == 0) break;
289  }
290  if (!ctrls[i]) goto missing_control;
291 
292  /*
293  * Get the value of the control.
294  */
295  ber = ber_init(&ctrls[i]->ldctl_value);
296  if (!ber) {
297  ERROR("Failed allocating ber to handle syncStateValue control");
298 
299  error:
300  if (ber) ber_free(ber, 1);
301  ldap_msgfree(msg);
302  return -1;
303  }
304 
305  bv_ret = ber_scanf(ber, "{em", &op, &entry_uuid);
306  if ((bv_ret == LBER_ERROR) || (entry_uuid.bv_len == 0)) {
307  ERROR("Malformed syncUUID value");
308  goto error;
309  }
310 
311  if (sync_new_cookie(&new_cookie, sync, ber) < 0) goto error;
312 
313  if (ber_scanf(ber, "}") == LBER_ERROR ) {
314  ERROR("Malformed syncStatevalue sequence");
315  goto error;
316  }
317 
318  switch (op) {
319  case SYNC_OP_PRESENT:
320  switch (sync->phase) {
321 
322  case SYNC_PHASE_INIT:
323  sync->phase = SYNC_PHASE_PRESENT;
324  break;
325 
326  case SYNC_PHASE_PRESENT:
327  break;
328 
329  default:
330  bad_phase:
331  ERROR("Entries with %s state are not allowed during refresh %s phase",
332  fr_table_str_by_value(sync_op_table, op, "<unknown>"),
333  fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"));
334  goto error;
335  }
336  break;
337 
338  case SYNC_OP_DELETE:
339  switch (sync->phase) {
340  case SYNC_PHASE_DELETE:
341  case SYNC_PHASE_DONE:
342  break;
343 
344  default:
345  goto bad_phase;
346  }
347  break;
348 
349  case SYNC_OP_ADD:
350  case SYNC_OP_MODIFY:
351  /*
352  * RFC 4533 is less than clear about when added or modified entries
353  * should be sent during the initial refresh stage.
354  * If this is the first thing we see, then it will be the Present
355  * phase.
356  * All other phases can receive added / modified entries, and those
357  * do not indicate a change in phase.
358  */
359  switch (sync->phase) {
360  case SYNC_PHASE_INIT:
361  sync->phase = SYNC_PHASE_PRESENT;
362  break;
363 
364  default:
365  break;
366  }
367  break;
368 
369 
370  default:
371  ERROR("Unknown entry state (%i)", op);
372  goto error;
373  }
374 
375  if (DEBUG_ENABLED3) {
376  char *entry_dn;
377  fr_value_box_t uuid_box;
378 
379  entry_dn = ldap_get_dn(sync->conn->handle, msg);
380  fr_ldap_berval_to_value_shallow(&uuid_box, &entry_uuid);
381 
382  DEBUG3("Processing %s (%s), dn \"%s\", entryUUID %pV",
383  fr_table_str_by_value(sync_ldap_msg_table, ldap_msgtype(msg), "<unknown>"),
384  fr_table_str_by_value(sync_op_table, op, "<unknown>"),
385  entry_dn ? entry_dn : "<unknown>",
386  &uuid_box);
387 
388  ldap_memfree(entry_dn);
389  }
390 
391  /*
392  * Send the appropriate packet type to process the message
393  */
394  switch (ldap_msgtype(msg)) {
395  case LDAP_RES_SEARCH_ENTRY:
396  ret = ldap_sync_entry_send(sync, (uint8_t const *)entry_uuid.bv_val, NULL, msg, op);
397  break;
398 
399  case LDAP_RES_SEARCH_REFERENCE:
400  /* TODO - handle references */
401  ldap_msgfree(msg);
402  break;
403 
404  default:
405  fr_assert(0);
406  }
407 
408  /*
409  * We have a new cookie - store it
410  */
411  if ((ret == 0) && new_cookie) {
412  ret = ldap_sync_cookie_store(sync, false);
413  }
414  ber_free(ber, 1);
415 
416  return ret;
417 }
418 
419 /** Handle a LDAP_RES_INTERMEDIATE (SyncInfo) response
420  *
421  * These allow the LDAP server to communicate sync state to clients
422  *
423  * The Sync Info Message is an LDAP Intermediate Response Message [RFC4511] where
424  * responseName is the object identifier 1.3.6.1.4.1.4203.1.9.1.4 and responseValue
425  * contains a BER-encoded syncInfoValue.
426  *
427  * syncInfoValue ::= CHOICE {
428  * newcookie [0] syncCookie,
429  * refreshDelete [1] SEQUENCE {
430  * cookie syncCookie OPTIONAL,
431  * refreshDone BOOLEAN DEFAULT TRUE
432  * },
433  * refreshPresent [2] SEQUENCE {
434  * cookie syncCookie OPTIONAL,
435  * refreshDone BOOLEAN DEFAULT TRUE
436  * },
437  * syncIdSet [3] SEQUENCE {
438  * cookie syncCookie OPTIONAL,
439  * refreshDeletes BOOLEAN DEFAULT FALSE,
440  * syncUUIDs SET OF syncUUID
441  * }
442  * }
443  *
444  * @param[in] sync message was associated with.
445  * @param[in] msg containing an entry to process.
446  * @param[in] ctrls associated with the msg.
447  * @return
448  * - 0 on success.
449  * - -1 on failure.
450  */
451 int rfc4533_sync_intermediate(sync_state_t *sync, LDAPMessage *msg, UNUSED LDAPControl **ctrls)
452 {
453  int ret, i;
454  char *oid = NULL;
455  struct berval *data = NULL;
456  BerElement *ber = NULL;
457  ber_len_t len;
458  ber_tag_t sync_info_tag;
459  int refresh_deletes = 0;
460  BerVarray sync_uuids = NULL;
461 
462  bool new_cookie;
463  int refresh_done = false;
464 
465  /*
466  * Extract data from the message. Setting freeit to 1 means the message
467  * will be freed after the extract.
468  */
469  ret = ldap_parse_intermediate(sync->conn->handle, msg, &oid, &data, NULL, 1);
470  if (!fr_cond_assert(ret == LDAP_SUCCESS)) return -1;
471 
472  if (!oid || (strcmp(oid, LDAP_SYNC_INFO) != 0)) {
473  WARN("Ignoring intermediateResult with unexpected OID \"%s\"", oid ? oid : "<unknown>");
474  return 0;
475  }
476 
477  ber = ber_init(data);
478  if (ber == NULL) {
479  ERROR("Failed allocating ber to handle syncInfo data");
480 
481  return -1;
482  }
483 
484  sync_info_tag = ber_peek_tag(ber, &len);
485  DEBUG3("Processing syncInfo (%s)", fr_table_str_by_value(sync_info_tag_table, sync_info_tag, "<unknown>"));
486 
487  switch (sync_info_tag) {
488  /*
489  * A new cookie to store.
490  * Typically provided when data in the directory has changed
491  * but those changes don't match the search.
492  */
493  case LDAP_TAG_SYNC_NEW_COOKIE:
494  if (sync_new_cookie(&new_cookie, sync, ber) < 0) {
495  error:
496  if (sync_uuids) ber_bvarray_free(sync_uuids);
497  if (ber) ber_free(ber, 1);
498  if (data) ber_bvfree(data);
499  if (oid) ldap_memfree(oid);
500 
501  return -1;
502  }
503 
504  if (!new_cookie) {
505  ERROR("Missing cookie value");
506  goto error;
507  }
508  break;
509 
510  /*
511  * During the initial refresh operation, indicates the
512  * end of the "present" phase.
513  */
514  case LDAP_TAG_SYNC_REFRESH_PRESENT:
515  switch (sync->phase) {
516  case SYNC_PHASE_INIT:
517  sync->phase = SYNC_PHASE_PRESENT;
518  break;
519 
520  case SYNC_PHASE_PRESENT:
521  break;
522 
523  default:
524  ERROR("Invalid refresh phase transition (%s->%s)",
525  fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"),
527  goto error;
528  }
529 
530  if (ber_scanf(ber, "{") == LBER_ERROR) {
531  ERROR("Malformed refreshPresent sequence");
532  goto error;
533  }
534 
535  if (sync_new_cookie(&new_cookie, sync, ber) < 0) goto error;
536 
537  if (ber_peek_tag(ber, &len) == LDAP_TAG_REFRESHDONE) {
538  if (ber_scanf(ber, "b", &refresh_done) == LBER_ERROR) {
539  ERROR("Malformed refresh sequence: Missing refreshDone tag value");
540  goto error;
541  }
542  } else {
543  refresh_done = true; /* Default (when absent) is true */
544  }
545 
546  if (ber_scanf(ber, "}") == LBER_ERROR ) {
547  ERROR("Malformed refreshPresent sequence");
548  goto error;
549  }
550 
551  if (refresh_done) sync->phase = SYNC_PHASE_DONE;
552  break;
553 
554  /*
555  * During the initial refresh operation, indicates the
556  * end of the "delete" phase.
557  */
558  case LDAP_TAG_SYNC_REFRESH_DELETE:
559  switch (sync->phase) {
560  case SYNC_PHASE_INIT:
561  case SYNC_PHASE_PRESENT:
562  sync->phase = SYNC_PHASE_DELETE;
563  break;
564 
565  case SYNC_PHASE_DELETE:
566  break;
567 
568  default:
569  ERROR("Invalid refresh phase transition (%s->%s)",
570  fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"),
572 
573  goto error;
574  }
575 
576  if (ber_scanf(ber, "{") == LBER_ERROR) {
577  ERROR("Malformed refreshPresent sequence");
578  goto error;
579  }
580 
581  if (sync_new_cookie(&new_cookie, sync, ber) < 0) goto error;
582 
583  if (ber_peek_tag(ber, &len) == LDAP_TAG_REFRESHDONE) {
584  if (ber_scanf(ber, "b", &refresh_done) == LBER_ERROR) {
585  ERROR("Malformed refresh sequence: Missing refreshDone tag value");
586  goto error;
587  }
588  } else {
589  refresh_done = true;
590  }
591 
592  if (ber_scanf(ber, "}") == LBER_ERROR ) {
593  ERROR("Malformed refreshPresent sequence");
594  goto error;
595  }
596  if (refresh_done) sync->phase = SYNC_PHASE_DONE;
597  break;
598 
599  /*
600  * An intermediate response message with the syncInfoValue containing
601  * a syncIDSet.
602  * If refreshDeletes is false, the list of UUIDs are "present"
603  * If refreshDeletes is true, the list of UUIDs are entries to be deleted
604  */
605  case LDAP_TAG_SYNC_ID_SET:
606  if (ber_scanf( ber, "{") == LBER_ERROR) {
607  ERROR("Malformed syncIDSet");
608  goto error;
609  }
610 
611  if (sync_new_cookie(&new_cookie, sync, ber) < 0) goto error;
612 
613  if (ber_peek_tag(ber, &len) == LDAP_TAG_REFRESHDELETES) {
614  if (ber_scanf(ber, "b", &refresh_deletes) == LBER_ERROR) {
615  ERROR("Malformed refresh_deletes tag");
616  goto error;
617  }
618  }
619 
620  if ((ber_scanf(ber, "[W]}", &sync_uuids) == LBER_ERROR) || !sync_uuids) {
621  ERROR("syncIDSet missing set of IDs");
622  goto error;
623  }
624 
625  if (refresh_deletes) {
626  /*
627  * refresh_deletes == true indicates we are starting, in or at the end
628  * of a delete phase.
629  */
630  switch (sync->phase) {
631  case SYNC_PHASE_INIT:
632  case SYNC_PHASE_PRESENT:
633  sync->phase = SYNC_PHASE_DELETE;
634  break;
635 
636  case SYNC_PHASE_DELETE:
637  break;
638 
639  default:
640  ERROR("Invalid refresh phase transition (%s->%s)",
641  fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"),
643  goto error;
644  }
645  } else {
646  /*
647  * refresh_deletes == false indicates we are starting, in or at the end
648  * of a present phase
649  */
650  switch (sync->phase) {
651  case SYNC_PHASE_INIT:
652  sync->phase = SYNC_PHASE_PRESENT;
653  break;
654 
655  case SYNC_PHASE_PRESENT:
656  break;
657 
658  default:
659  ERROR("Invalid refresh phase transition (%s->%s)",
660  fr_table_str_by_value(sync_phase_table, sync->phase, "<unknown>"),
662  goto error;
663  }
664  }
665 
666  /*
667  * Process the list of UUIDs provided.
668  * The value of refresh_deletes indicates whether these are "present" or "delete"
669  */
670  for (i = 0; sync_uuids[i].bv_val != NULL; i++) {
671  if (sync_uuids[i].bv_len != SYNC_UUID_LENGTH) {
672  ERROR("Invalid entryUUID length, expected " STRINGIFY(SYNC_UUID_LENGTH) " "
673  "bytes got %zu bytes", sync_uuids[i].bv_len);
674  goto error;
675  }
676  ret = ldap_sync_entry_send(sync, (uint8_t const *)sync_uuids[i].bv_val, NULL, NULL,
677  (refresh_deletes ? SYNC_OP_DELETE : SYNC_OP_PRESENT));
678  if (ret < 0) goto error;
679  }
680 
681  ber_bvarray_free(sync_uuids);
682  sync_uuids = NULL;
683  break;
684 
685  default:
686  ERROR("Invalid syncInfo tag %lu", sync_info_tag);
687  goto error;
688 
689  }
690 
691  if (new_cookie) {
692  ret = ldap_sync_cookie_store(sync, false);
693  }
694 
695  if (ber) ber_free(ber, 1);
696  if (oid) ldap_memfree(oid);
697  if (data) ber_bvfree(data);
698 
699  return ret;
700 }
701 
702 /** Handle result code of e-syncRefreshRequired
703  *
704  * If the server wishes to indicate that a refresh is required, it sends a searchResultDone
705  * message with the result code e-syncRefreshRequired result code. Any cookie provided
706  * should be used on a query to re-start the sync. If no cookie is provided, the new
707  * query should be performed without a cookie to get a full refresh.
708  *
709  * @param[in] sync message was associated with.
710  * @param[in] msg requesting the refresh.
711  * @param[in] ctrls associated with the msg.
712  * @return
713  * - 0 on success.
714  * - -1 on failure.
715  */
716 int rfc4533_sync_refresh_required(sync_state_t *sync, LDAPMessage *msg, LDAPControl **ctrls)
717 {
718  int refresh_deletes = 0;
719 
720  int i;
721  BerElement *ber = NULL;
722  ber_len_t len;
723  bool new_cookie = false;
724 
725  fr_assert(sync);
726  fr_assert(sync->conn);
727 
728  ldap_msgfree(msg);
729 
730  /*
731  * We may or may not have controls.
732  * If the server wants the refresh to occur from a specific change it will
733  * send a cookie which should be used when re-starting the sync query.
734  */
735  if (ctrls) {
736  for (i = 0; ctrls[i] != NULL; i++) {
737  if (strcmp(ctrls[i]->ldctl_oid, LDAP_CONTROL_SYNC_DONE) == 0) break;
738  }
739 
740  if (ctrls[i]) {
741  ber = ber_init(&ctrls[i]->ldctl_value);
742  if (!ber) {
743  ERROR("Failed allocating ber to handle syncDoneValue control");
744 
745  error:
746  if (ber) ber_free(ber, 1);
747  return -1;
748  }
749 
750  if (ber_scanf( ber, "{" /*"}"*/) == LBER_ERROR) goto error;
751 
752  if (sync_new_cookie(&new_cookie, sync, ber) < 0) goto error;
753 
754  if (ber_peek_tag(ber, &len) == LDAP_TAG_REFRESHDELETES) {
755  if (ber_scanf(ber, "b", &refresh_deletes) == LBER_ERROR) {
756  ERROR("Malformed refresh sequence: Missing refreshDeletes tag value");
757  goto error;
758  }
759  }
760 
761  if (ber_scanf( ber, /*"{"*/ "}" ) == LBER_ERROR) {
762  ERROR("Malformed syncDoneValue sequence");
763  goto error;
764  }
765 
766  ber_free(ber, 1);
767  }
768  }
769 
770  /*
771  * If we received e-syncRefreshRequired but no cookie the server is
772  * indicating we should clear the cookie before restarting the sync.
773  */
774  if (!new_cookie) {
775  talloc_free(sync->cookie);
776  sync->cookie = NULL;
777  new_cookie = true;
778  }
779 
780  return ldap_sync_cookie_store(sync, true);
781 }
log_entry msg
Definition: acutest.h:794
#define L(_str)
Helper for initialising arrays of string literals.
Definition: build.h:207
#define STRINGIFY(x)
Definition: build.h:195
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:137
#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:1768
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:645
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
LDAP * handle
libldap handle.
Definition: base.h:331
fr_connection_t * conn
Connection state handle.
Definition: base.h:343
void * uctx
User data associated with the handle.
Definition: base.h:352
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition: base.h:580
Tracks the state of a libldap connection handle.
Definition: base.h:330
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:528
#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
Definition: merged_model.c:30
static const conf_parser_t config[]
Definition: base.c:188
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.
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.
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.
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 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:624
#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:716
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:259
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:175
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:451
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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:253
An element in a lexicographically sorted array of name to num mappings.
Definition: table.h:45
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:280
static fr_slen_t data
Definition: value.h:1259
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:279