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: 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  */
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  * - -2 same as existing cookie.
175  */
176 static 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  */
260 int 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:
324  sync->phase = SYNC_PHASE_PRESENT;
325  break;
326 
327  case SYNC_PHASE_PRESENT:
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) {
341  case SYNC_PHASE_DELETE:
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:
362  sync->phase = SYNC_PHASE_PRESENT;
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  */
452 int 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:
518  sync->phase = SYNC_PHASE_PRESENT;
519  break;
520 
521  case SYNC_PHASE_PRESENT:
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:
562  case SYNC_PHASE_PRESENT:
563  sync->phase = SYNC_PHASE_DELETE;
564  break;
565 
566  case SYNC_PHASE_DELETE:
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:
633  case SYNC_PHASE_PRESENT:
634  sync->phase = SYNC_PHASE_DELETE;
635  break;
636 
637  case SYNC_PHASE_DELETE:
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:
653  sync->phase = SYNC_PHASE_PRESENT;
654  break;
655 
656  case SYNC_PHASE_PRESENT:
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  */
717 int 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: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:139
#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
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:333
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: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: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.
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
#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
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
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
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: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