The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
active_directory.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: 56b1537922e129ff5266050e4a748e9f6d56f38c $
19  * @file active_directory.c
20  * @brief LDAP sync callback functions for Active Directory servers.
21  *
22  * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
23  */
25 
26 #define LOG_PREFIX "ldap_sync_ad"
27 
28 #include "active_directory.h"
29 #include <freeradius-devel/util/debug.h>
30 
31 static int active_directory_sync_attr_add(char const *attr, void *uctx)
32 {
34 
35  return ldap_sync_conf_attr_add(config, attr);
36 }
37 
38 /** Allocate a sync state structure and issue the search
39  *
40  * Active Directory uses its own control to mark persistent searches.
41  * In addition we add the control to request the return of deleted objects
42  * which allows searches specifically on the Deleted Objects container.
43  *
44  * Neither of these controls take values.
45  *
46  * @param[in] conn Connection to issue the search request on.
47  * @param[in] sync_no number of the sync in the array of configs.
48  * @param[in] inst instance of ldap_sync this query relates to.
49  * @param[in] cookie unused for Active Directory
50  * @return
51  * - 0 on success
52  * - -1 on failure
53  */
55  UNUSED uint8_t const *cookie)
56 {
57  static char const *notify_oid = LDAP_SERVER_NOTIFICATION_OID;
58  static char const *deleted_oid = LDAP_SERVER_SHOW_DELETED_OID;
59  LDAPControl ctrl = {0}, ctrl2 = {0}, *ctrls[3] = { &ctrl, &ctrl2, NULL };
60  fr_slen_t ret;
61  fr_ldap_rcode_t rcode;
62  sync_state_t *sync;
63  fr_rb_tree_t *tree;
64  char const *filter = NULL;
65  sync_config_t const *config = inst->sync_config[sync_no];
66 
67  fr_assert(conn);
69 
70  /*
71  * Allocate or retrieve the tree of outstanding msgids
72  * these are specific to the connection.
73  */
74  if (!conn->uctx) {
75  MEM(tree = fr_rb_inline_talloc_alloc(conn, sync_state_t, node, sync_state_cmp, NULL));
76  conn->uctx = tree;
77  } else {
78  tree = talloc_get_type_abort(conn->uctx, fr_rb_tree_t);
79  }
80 
81  sync = sync_state_alloc(tree, conn, inst, sync_no, config);
82 
83  /*
84  * Notification control - marks this as a persistent search.
85  */
86  memcpy(&ctrl.ldctl_oid, &notify_oid, sizeof(ctrl.ldctl_oid));
87  ctrl.ldctl_value.bv_len = 0;
88  ctrl.ldctl_value.bv_val = NULL;
89  ctrl.ldctl_iscritical = 1;
90 
91  /*
92  * Show deleted control - instructs the server to include deleted
93  * objects in the reply.
94  */
95  memcpy(&ctrl2.ldctl_oid, &deleted_oid, sizeof(ctrl2.ldctl_oid));
96  ctrl2.ldctl_value.bv_len = 0;
97  ctrl2.ldctl_value.bv_val = NULL;
98  ctrl2.ldctl_iscritical = 1;
99  ctrls[1] = &ctrl2;
100  ctrl2.ldctl_iscritical = 1;
101 
102  /*
103  * Active Directory only takes the filter (objectClass=*) when the
104  * notification control is used. To compensate for this we parse the
105  * filter and implement our own basic version of a filter.
106  */
107  if (strcasecmp(sync->config->filter, "(objectClass=*)") != 0) {
108  DEBUG2("LDAP filter %s does not match Active Directory requirements, parsing for local filtering.",
109  sync->config->filter);
110  filter = "(objectClass=*)";
111  ret = fr_ldap_filter_parse(sync, &sync->filter, &FR_SBUFF_IN(config->filter, strlen(config->filter)),
113  if (ret < 0) {
114  CONF_ITEM *ci;
115 
116  ci = (CONF_ITEM *)cf_pair_find(sync->config->cs, "filter");
117  cf_log_err(ci, "Invalid LDAP filter");
118  EMARKER(config->filter, ret + 1, fr_strerror());
119 
120  error:
121  talloc_free(sync);
122  return -1;
123  }
124  }
125 
126  /*
127  * The isDeleted attribute needs to be in the requested list
128  * in order to detect if a notification is because an entry is deleted
129  */
131 
132  rcode = fr_ldap_search_async(&sync->msgid, NULL, conn, config->base_dn, config->scope,
133  filter ? filter : config->filter, config->attrs, ctrls, NULL);
134 
135  if (rcode != LDAP_PROC_SUCCESS) goto error;
136 
137  if (!fr_rb_insert(tree, sync)) {
138  ERROR("Duplicate sync (msgid %i)", sync->msgid);
139  goto error;
140  }
141 
142  DEBUG3("Sync created with base dn \"%s\", filter \"%s\", msgid %i",
143  sync->config->base_dn, sync->config->filter, sync->msgid);
144 
145  trigger_exec(unlang_interpret_get_thread_default(), config->cs, "ldap_sync.start", true, &sync->trigger_args);
146 
147  return 0;
148 }
149 /** Handle a LDAP_RES_SEARCH_ENTRY (SearchResultEntry) response
150  *
151  * This version is specific to Active Directory, which does things its own way.
152  *
153  * In response to a search request containing the Server Notification Control, Active Directory
154  * will initially return nothing.
155  *
156  * Then as entries matching the query are changed, SearchResultEntry messages will be returned
157  * for the matching entries. There is no indication as to whether the change is an addition or
158  * a modification.
159  *
160  * In order to be notified about deleted objects, the Recycle Bin optional feature must be enabled
161  * and the search must have a base DN which includes the Deleted Objects container, then,
162  * an attribute isDeleted will indicate the state of the entry.
163  *
164  * @param[in] sync message was associated with.
165  * @param[in] msg containing an entry to process.
166  * @param[in] ctrls unused LDAP controls
167  * @return
168  * - 0 on success.
169  * - -1 on failure.
170  */
171 int active_directory_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, UNUSED LDAPControl **ctrls)
172 {
173  int count, i, ret = 0;
175  struct berval **values;
176 
177  fr_assert(sync->conn);
178  fr_assert(sync);
179  fr_assert(msg);
180 
181  /*
182  * Implement our own filtering if the config has specified
183  * something other than (objectClass=*)
184  */
185  if (sync->filter && !fr_ldap_filter_eval(sync->filter, sync->conn, msg)) {
186  DEBUG2("Discarding packet which fails LDAP filter");
187  ldap_msgfree(msg);
188  return 0;
189  }
190 
191  /*
192  * Look for an "isDeleted" attribute - this is Active Directory's indicator
193  * for a deleted object - process these through the recv Delete section.
194  */
195  values = ldap_get_values_len(sync->conn->handle, msg, "isDeleted");
196  count = ldap_count_values_len(values);
197  for (i = 0; i < count; i++) {
198  if ((values[i]->bv_len == 4) && (strncmp(values[i]->bv_val, "TRUE", 4) == 0)) {
199  op = SYNC_OP_DELETE;
200  break;
201  }
202  }
203  ldap_value_free_len(values);
204 
205  /*
206  * Send the packet with the entry change notification
207  */
208  ret = ldap_sync_entry_send(sync, NULL, NULL, msg, op);
209 
210  return ret;
211 }
static int active_directory_sync_attr_add(char const *attr, void *uctx)
int active_directory_sync_state_init(fr_ldap_connection_t *conn, size_t sync_no, proto_ldap_sync_t const *inst, UNUSED uint8_t const *cookie)
Allocate a sync state structure and issue the search.
int active_directory_sync_search_entry(sync_state_t *sync, LDAPMessage *msg, UNUSED LDAPControl **ctrls)
Handle a LDAP_RES_SEARCH_ENTRY (SearchResultEntry) response.
log_entry msg
Definition: acutest.h:794
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition: build.h:165
#define USES_APPLE_DEPRECATED_API
Definition: build.h:468
#define UNUSED
Definition: build.h:313
Common header for all CONF_* types.
Definition: cf_priv.h:49
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
Definition: cf_util.c:1439
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:289
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
unlang_interpret_t * unlang_interpret_get_thread_default(void)
Get the default interpreter for this thread.
Definition: interpret.c:1787
fr_slen_t fr_ldap_filter_parse(TALLOC_CTX *ctx, fr_dlist_head_t **root, fr_sbuff_t *filter, filter_attr_check_t attr_check, void *uctx)
Parse an LDAP filter into its component nodes.
Definition: filter.c:343
#define LDAP_SERVER_NOTIFICATION_OID
OID of Active Directory control for.
Definition: base.h:115
LDAP * handle
libldap handle.
Definition: base.h:333
bool fr_ldap_filter_eval(fr_dlist_head_t *root, fr_ldap_connection_t *conn, LDAPMessage *msg)
Evaluate an LDAP filter.
Definition: filter.c:583
#define LDAP_SERVER_SHOW_DELETED_OID
OID of Active Directory control which.
Definition: base.h:117
void * uctx
User data associated with the handle.
Definition: base.h:354
fr_ldap_rcode_t
Codes returned by fr_ldap internal functions.
Definition: base.h:582
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition: base.h:585
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 DEBUG3(_fmt,...)
Definition: log.h:266
#define EMARKER(_str, _marker_idx, _marker)
Definition: log.h:232
talloc_free(reap)
unsigned char uint8_t
Definition: merged_model.c:30
ssize_t fr_slen_t
Definition: merged_model.c:35
int strcasecmp(char *s1, char *s2)
Definition: missing.c:66
static const conf_parser_t config[]
Definition: base.c:183
int ldap_sync_conf_attr_add(sync_config_t *config, char const *attr)
Check if an attribute is in the config list and add if not present.
char const * filter
Filter to retrieve only user objects.
sync_op_t
Operations to perform on entries.
@ SYNC_OP_MODIFY
Entry should be updated in our copy.
@ SYNC_OP_DELETE
Entry should be deleted from our copy.
char const * base_dn
DN to search for users under.
CONF_SECTION * cs
Config section where this sync was defined.
An instance of a proto_ldap_sync listen section.
Areas of the directory to receive notifications for.
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.
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.
int8_t sync_state_cmp(void const *one, void const *two)
Compare two sync state structures on msgid.
fr_dlist_head_t * filter
Parsed filter to be applied on the network side before passing packets to the worker.
int msgid
The unique identifier for this sync session.
sync_config_t const * config
Configuration for this sync.
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 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
#define FR_SBUFF_IN(_start, _len_or_end)
return count
Definition: module.c:163
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
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
char const * fr_strerror(void)
Get the last library error.
Definition: strerror.c:554