The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
referral.c
Go to the documentation of this file.
1/*
2 * This program is 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 (at
5 * 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: 8afee84520f0d2c547b6d8c0d5b36d51d7f51d52 $
19 * @file lib/ldap/referral.c
20 * @brief Functions to handle ldap referrals
21 *
22 * @author Nick Porter <nick.porter@networkradius.com>
23 * @copyright 2021 The FreeRADIUS Server Project.
24 */
25RCSID("$Id: 8afee84520f0d2c547b6d8c0d5b36d51d7f51d52 $")
26
27#include <freeradius-devel/ldap/base.h>
28
29
30/** Clear up a fr_ldap_referral_t
31 *
32 * If there is a parsed referral_url, that must be freed using libldap's ldap_free_urldesc
33 */
35{
36 if (referral->referral_url) ldap_free_urldesc(referral->referral_url);
37 if (referral->host_uri) ldap_memfree(referral->host_uri);
38 return 0;
39}
40
41/** Allocate a new structure to handle an LDAP referral, setting the destructor
42 *
43 * @param[in] ctx to allocate the referral in
44 * @param[in] request the LDAP query relates to.
45 * @return
46 * - a new referral structure on success
47 * - NULL on failure
48 */
50{
51 fr_ldap_referral_t *referral;
52
53 referral = talloc_zero(ctx, fr_ldap_referral_t);
54 if (!referral) {
55 PERROR("Failed to allocate LDAP referral container");
56 return NULL;
57 }
58 referral->request = request;
59 talloc_set_destructor(referral, _fr_ldap_referral_free);
60
61 return referral;
62}
63
64/** Callback to send LDAP referral queries when a trunk becomes active
65 *
66 */
67CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private trunk_t trips --fsanitize=function*/
69 UNUSED trunk_state_t state, void *uctx)
70{
71 fr_ldap_referral_t *referral = talloc_get_type_abort(uctx, fr_ldap_referral_t);
72 fr_ldap_query_t *query = referral->query;
73 request_t *request = referral->request;
74
75 /*
76 * If referral is set, then another LDAP trunk has gone active first and sent the referral
77 */
78 if (query->referral) return;
79
80 /*
81 * Enqueue referral query on active trunk connection
82 */
83 query->referral = referral;
84 switch (trunk_request_enqueue(&query->treq, referral->ttrunk->trunk, request, query, NULL)) {
87 break;
88
89 default:
90 ROPTIONAL(RERROR, ERROR, "Failed enqueueing pending LDAP referral");
91 query->ret = LDAP_RESULT_ERROR;
92 if (request) unlang_interpret_mark_runnable(request);
93 return;
94 }
95
96 ROPTIONAL(RDEBUG3, DEBUG3, "Pending LDAP referral query queued on active trunk");
97}
98
99
100/** Follow an LDAP referral
101 *
102 * The returned list of LDAP referrals should already be in query->referrals.
103 * We check all the possible referrals and look for one where there already
104 * is an active trunk connection.
105 *
106 * @param t Thread running the query.
107 * @param request related to the query.
108 * @param query whose result was one or more referral URLs.
109 * @return
110 * - 0 on success.
111 * - < 0 on failure.
112 */
114{
116 fr_ldap_thread_trunk_t *ttrunk = NULL;
117 int referral_no = -1;
118 fr_ldap_referral_t *referral;
119 LDAPURLDesc temp_desc;
120
122 query->treq = NULL;
123
124 if (query->referral_depth > 1) {
125 /*
126 * If we've already parsed a referral, clear the existing list of followers.
127 */
129 query->referral = NULL;
130 } else {
131 /*
132 * Otherwise initialise the list header for followers.
133 */
135 }
136
137 while (query->referral_urls[++referral_no]) {
138 if (!ldap_is_ldap_url(query->referral_urls[referral_no])) {
139 ROPTIONAL(RERROR, ERROR, "Referral %s does not look like an LDAP URL",
140 query->referral_urls[referral_no]);
141 continue;
142 }
143
144 referral = fr_ldap_referral_alloc(query, request);
145 if (!referral) continue;
146
147 referral->query = query;
148
149 if (ldap_url_parse(query->referral_urls[referral_no], &referral->referral_url)) {
151 "Failed parsing referral LDAP URL %s", query->referral_urls[referral_no]);
152 free_referral:
153 talloc_free(referral);
154 continue;
155 }
156
157 temp_desc = (LDAPURLDesc){
158 .lud_scheme = referral->referral_url->lud_scheme,
159 .lud_host = referral->referral_url->lud_host,
160 .lud_port = referral->referral_url->lud_port,
161 .lud_scope = -1
162 };
163 referral->host_uri = ldap_url_desc2str(&temp_desc);
164 if (!referral->host_uri) {
166 "Failed building LDAP host URI from %s", query->referral_urls[referral_no]);
167 }
168
169 if (config->use_referral_credentials) {
170 char **ext;
171
172 /*
173 * If there are no extensions, OpenLDAP doesn't
174 * bother allocating an array.
175 */
176 for (ext = referral->referral_url->lud_exts; ext && *ext; ext++) {
177 char const *p;
178 bool critical = false;
179
180 p = *ext;
181
182 if (*p == '!') {
183 critical = true;
184 p++;
185 }
186
187 /*
188 * LDAP Parse URL unescapes the extensions for us
189 */
192 p = strchr(p, '=');
193 if (!p) {
194 bad_ext:
196 "Failed parsing extension \"%s\": "
197 "No attribute/value delimiter '='", *ext);
198 goto free_referral;
199 }
200 referral->identity = p + 1;
201 break;
202
203 case LDAP_EXT_BINDPW:
204 p = strchr(p, '=');
205 if (!p) goto bad_ext;
206 referral->password = p + 1;
207 break;
208
209 default:
210 if (critical) {
212 "Failed parsing critical extension \"%s\": "
213 "Not supported by FreeRADIUS", *ext);
214 goto free_referral;
215 }
216 ROPTIONAL(RDEBUG2, DEBUG2, "Skipping unsupported extension \"%s\"", *ext);
217 continue;
218 }
219 }
220 } else {
221 if (config->rebind) {
222 referral->identity = config->admin_identity;
223 referral->password = config->admin_password;
224 }
225 }
226
227 fr_dlist_insert_tail(&query->referrals, referral);
228 if (fr_thread_ldap_trunk_state(t, referral->host_uri,
229 referral->identity) != TRUNK_STATE_ACTIVE) {
231 "No active LDAP trunk for URI %s, bound as %s",
232 referral->host_uri, referral->identity);
233 continue;
234 }
235
236 ttrunk = fr_thread_ldap_trunk_get(t, referral->host_uri, referral->identity,
237 referral->password, request, config);
238
239 if (!ttrunk) {
240 ROPTIONAL(RERROR, ERROR, "Unable to connect to LDAP referral URL");
241 fr_dlist_talloc_free_item(&query->referrals, referral);
242 continue;
243 }
244
245 /*
246 * We have an active trunk enqueue the request
247 */
248 query->referral = referral;
249 switch (trunk_request_enqueue(&query->treq, ttrunk->trunk, request, query, NULL)) {
250 case TRUNK_ENQUEUE_OK:
252 break;
253
254 default:
255 ROPTIONAL(RERROR, ERROR, "Failed to enqueue request for referral");
256 goto free_referral;
257 }
258 return 0;
259 }
260
261 /*
262 * None of the referrals parsed successfully
263 */
264 if (fr_dlist_num_elements(&query->referrals) == 0) {
265 ROPTIONAL(RERROR, ERROR, "No valid LDAP referrals to follow");
266 return -1;
267 }
268
269 /*
270 * We have parsed referrals, but none of them matched an existing active connection.
271 * Launch new trunks with callbacks so the first to become active will run the query.
272 */
273 referral = NULL;
274 while ((referral = fr_dlist_next(&query->referrals, referral))) {
275 ttrunk = fr_thread_ldap_trunk_get(t, referral->host_uri, referral->identity,
276 referral->password, request, config);
277 if (!ttrunk) {
278 fr_dlist_talloc_free_item(&query->referrals, referral);
279 continue;
280 }
281 referral->ttrunk = ttrunk;
283 ROPTIONAL(RDEBUG4, DEBUG4, "Watch inserted to send referral query on active trunk");
284 }
285
286 return 0;
287}
288
289/** Follow an alternative LDAP referral
290 *
291 * If an initial chase of an LDAP referral results in an error being returned
292 * this function can be used to attempt one of the other referral URLs given
293 * in the initial query results.
294 *
295 * The initial use of fr_ldap_referral_follow may have launched trunks for
296 * any referral URLs which parsed successfully, so this starts by looking
297 * for the first which has an active state and sends the query that way.
298 *
299 * If no active trunks match the remaining servers listed in referrals then
300 * new trunks are launched with watchers to send the query on the first
301 * active trunk.
302 *
303 * @param t Thread running the query.
304 * @param request the query relates to.
305 * @param query whose referrals are being chased.
306 * @return
307 * - 0 on success.
308 * - < 0 on failure.
309 */
311{
313 fr_ldap_referral_t *referral = NULL;
315
317 query->treq = NULL;
318
319 while ((referral = fr_dlist_next(&query->referrals, referral))) {
320 if (fr_thread_ldap_trunk_state(t, referral->host_uri,
321 referral->identity) != TRUNK_STATE_ACTIVE) {
322 ROPTIONAL(RDEBUG3, DEBUG3, "No active LDAP trunk for URI %s, bind DN %s",
323 referral->host_uri, referral->identity);
324 continue;
325 }
326
327 ttrunk = fr_thread_ldap_trunk_get(t, referral->host_uri, referral->identity,
328 referral->password, request, config);
329
330 if (!ttrunk) {
331 ROPTIONAL(RERROR, ERROR, "Unable to connect to LDAP referral URL");
332 fr_dlist_talloc_free_item(&query->referrals, referral);
333 continue;
334 }
335
336 /*
337 * We have an active trunk enqueue the request
338 */
339 query->referral = referral;
340 switch(trunk_request_enqueue(&query->treq, ttrunk->trunk, request, query, NULL)) {
341 case TRUNK_ENQUEUE_OK:
343 break;
344
345 default:
346 ROPTIONAL(RERROR, ERROR, "Failed to enqueue request for referral");
347 continue;
348 }
349 return 0;
350 }
351
352 /*
353 * None of the referrals parsed successfully
354 */
355 if (fr_dlist_num_elements(&query->referrals) == 0) {
356 ROPTIONAL(RERROR, ERROR, "No valid LDAP referrals to follow");
357 return -1;
358 }
359
360 /*
361 * None of the remaining referrals have an active trunk.
362 * Launch new trunks with callbacks so the first to become active will run the query.
363 */
364 referral = NULL;
365 while ((referral = fr_dlist_next(&query->referrals, referral))) {
366 ttrunk = fr_thread_ldap_trunk_get(t, referral->host_uri, referral->identity,
367 referral->password, request, config);
368 if (!ttrunk) {
369 fr_dlist_talloc_free_item(&query->referrals, referral);
370 continue;
371 }
372 referral->ttrunk = ttrunk;
374 ROPTIONAL(RDEBUG4, DEBUG4, "Watch inserted to send referral query on active trunk");
375 }
376
377 return 0;
378}
#define RCSID(id)
Definition build.h:483
#define CC_NO_UBSAN(_sanitize)
Definition build.h:426
#define UNUSED
Definition build.h:315
#define ERROR(fmt,...)
Definition dhcpclient.c:41
static void fr_dlist_talloc_free(fr_dlist_head_t *head)
Free all items in a doubly linked list (with talloc)
Definition dlist.h:908
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition dlist.h:939
static void * fr_dlist_talloc_free_item(fr_dlist_head_t *list_head, void *ptr)
Free the item specified.
Definition dlist.h:876
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:378
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:275
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:555
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1359
LDAPURLDesc * referral_url
URL for the referral.
Definition base.h:482
char * host_uri
Host URI used for referral connection.
Definition base.h:483
fr_ldap_config_t * config
Module instance config.
Definition base.h:383
char ** referral_urls
Referral results to follow.
Definition base.h:461
uint16_t referral_depth
How many referrals we have followed.
Definition base.h:463
fr_dlist_head_t referrals
List of parsed referrals.
Definition base.h:462
fr_ldap_result_code_t ret
Result code.
Definition base.h:470
request_t * request
Request this referral relates to.
Definition base.h:487
trunk_request_t * treq
Trunk request this query is associated with.
Definition base.h:456
fr_ldap_thread_trunk_t * fr_thread_ldap_trunk_get(fr_ldap_thread_t *thread, char const *uri, char const *bind_dn, char const *bind_password, request_t *request, fr_ldap_config_t const *config)
Find a thread specific LDAP connection for a specific URI / bind DN.
Definition connection.c:918
char const * identity
Bind identity for referral connection.
Definition base.h:484
fr_ldap_query_t * query
Query this referral relates to.
Definition base.h:481
@ LDAP_RESULT_ERROR
A general error occurred.
Definition base.h:191
trunk_state_t fr_thread_ldap_trunk_state(fr_ldap_thread_t *thread, char const *uri, char const *bind_dn)
Lookup the state of a thread specific LDAP connection trunk for a specific URI / bind DN.
Definition connection.c:996
char const * password
Bind password for referral connection.
Definition base.h:485
trunk_t * trunk
Connection trunk.
Definition base.h:405
fr_ldap_thread_trunk_t * ttrunk
Trunk this referral should use.
Definition base.h:486
fr_ldap_referral_t * referral
Referral actually being followed.
Definition base.h:464
@ LDAP_EXT_BINDPW
Specifies the password for an LDAP bind.
Definition base.h:125
@ LDAP_EXT_UNSUPPORTED
Unsupported extension.
Definition base.h:123
@ LDAP_EXT_BINDNAME
Specifies the user DN or name for an LDAP bind.
Definition base.h:124
Connection configuration.
Definition base.h:221
LDAP query structure.
Definition base.h:422
Parsed LDAP referral structure.
Definition base.h:479
Thread specific structure to manage LDAP trunk connections.
Definition base.h:381
Thread LDAP trunk structure.
Definition base.h:399
fr_table_num_sorted_t const fr_ldap_supported_extensions[]
Definition base.c:60
#define PERROR(_fmt,...)
Definition log.h:228
#define DEBUG3(_fmt,...)
Definition log.h:266
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:528
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RERROR(fmt,...)
Definition log.h:298
#define DEBUG4(_fmt,...)
Definition log.h:267
#define RDEBUG4(fmt,...)
Definition log.h:344
talloc_free(reap)
static const conf_parser_t config[]
Definition base.c:183
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define DEBUG2(fmt,...)
Definition radclient.h:43
static int _fr_ldap_referral_free(fr_ldap_referral_t *referral)
Clear up a fr_ldap_referral_t.
Definition referral.c:34
static void _ldap_referral_send(UNUSED trunk_t *trunk, UNUSED trunk_state_t prev, UNUSED trunk_state_t state, void *uctx)
Callback to send LDAP referral queries when a trunk becomes active.
Definition referral.c:68
int fr_ldap_referral_follow(fr_ldap_thread_t *t, request_t *request, fr_ldap_query_t *query)
Follow an LDAP referral.
Definition referral.c:113
fr_ldap_referral_t * fr_ldap_referral_alloc(TALLOC_CTX *ctx, request_t *request)
Allocate a new structure to handle an LDAP referral, setting the destructor.
Definition referral.c:49
int fr_ldap_referral_next(fr_ldap_thread_t *t, request_t *request, fr_ldap_query_t *query)
Follow an alternative LDAP referral.
Definition referral.c:310
#define fr_table_value_by_substr(_table, _name, _name_len, _def)
Convert a partial string to a value using an ordered or sorted table.
Definition table.h:693
trunk_watch_entry_t * trunk_add_watch(trunk_t *trunk, trunk_state_t state, trunk_watch_t watch, bool oneshot, void const *uctx)
Add a watch entry to the trunk state list.
Definition trunk.c:865
trunk_enqueue_t trunk_request_enqueue(trunk_request_t **treq_out, trunk_t *trunk, request_t *request, void *preq, void *rctx)
Enqueue a request that needs data written to the trunk.
Definition trunk.c:2587
void trunk_request_signal_complete(trunk_request_t *treq)
Signal that a trunk request is complete.
Definition trunk.c:2094
Main trunk management handle.
Definition trunk.c:198
trunk_state_t
Definition trunk.h:62
@ TRUNK_STATE_ACTIVE
Trunk has active connections.
Definition trunk.h:64
@ TRUNK_ENQUEUE_OK
Operation was successful.
Definition trunk.h:150
@ TRUNK_ENQUEUE_IN_BACKLOG
Request should be enqueued in backlog.
Definition trunk.h:149