The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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
31static int active_directory_sync_attr_add(char const *attr, void *uctx)
32{
33 sync_config_t *config = uctx;
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 */
171int 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:167
#define USES_APPLE_DEPRECATED_API
Definition build.h:470
#define UNUSED
Definition build.h:315
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
#define MEM(x)
Definition debug.h:36
#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 persistent search.
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 enables searching for deleted objects.
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:529
#define DEBUG3(_fmt,...)
Definition log.h:266
#define EMARKER(_str, _marker_idx, _marker)
Definition log.h:232
talloc_free(reap)
unsigned char uint8_t
ssize_t fr_slen_t
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.
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.
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 fr_assert(_expr)
Definition rad_assert.h:38
#define DEBUG2(fmt,...)
Definition radclient.h:43
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
#define FR_SBUFF_IN(_start, _len_or_end)
return count
Definition module.c:163
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