The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
profile.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 (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: ba15fdfbf03afdbe5c626b0c0a75884671309b3f $
19 * @file rlm_ldap.c
20 * @brief LDAP authorization and authentication module.
21 *
22 * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 * @author Alan DeKok (aland@freeradius.org)
24 *
25 * @copyright 2012,2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
26 * @copyright 2013,2015 Network RADIUS SAS (legal@networkradius.com)
27 * @copyright 2012 Alan DeKok (aland@freeradius.org)
28 * @copyright 1999-2013 The FreeRADIUS Server Project.
29 */
30RCSID("$Id: ba15fdfbf03afdbe5c626b0c0a75884671309b3f $")
31
33
34#include "rlm_ldap.h"
35#include <freeradius-devel/ldap/conf.h>
36
37#include <freeradius-devel/server/map_proc.h>
38#include <freeradius-devel/server/module_rlm.h>
39
40/** Holds state of in progress async profile lookups
41 *
42 */
43typedef struct {
44 fr_ldap_result_code_t *ret; //!< Result of the query and applying the map.
45 int *applied; //!< Number of profiles applied.
47 char const *dn; //!< DN of the profile object being retrieved.
48 ///< NULL when all profiles are retrieved by one search.
51 fr_ldap_thread_trunk_t *ttrunk; //!< Trunk used for profile searches.
52 char const *filter; //!< Filter to apply to profile searches.
53 char const * const *next; //!< Next profile DN to search for (search_mode = seq).
55
56/** Apply the attribute map to a single profile entry
57 *
58 * @param[out] fallthrough Whether processing should continue to the next profile entry.
59 * @param[in] request Current request.
60 * @param[in] profile_ctx Profile lookup state.
61 * @param[in] handle libldap handle the result was received on.
62 * @param[in] entry Profile object to apply.
63 */
64static void ldap_profile_entry_map(bool *fallthrough, request_t *request, ldap_profile_ctx_t *profile_ctx,
65 LDAP *handle, LDAPMessage *entry)
66{
67 int ret;
68
69 // Set fallthrough to the configured default
70 *fallthrough = profile_ctx->inst->profile.fallthrough_def;
71
72 ret = fr_ldap_map_do(request, profile_ctx->inst->profile.check_attr, profile_ctx->inst->valuepair_attr,
73 profile_ctx->expanded, entry);
74 if (ret < 0) {
75 if (profile_ctx->ret) *profile_ctx->ret = LDAP_RESULT_ERROR;
76 } else {
77 if (profile_ctx->applied) *profile_ctx->applied += ret;
78 }
79
80 if (profile_ctx->inst->profile.fallthrough_attr) {
81 struct berval value_bv;
82 int count;
83 char *value;
84 xlat_exp_head_t *cond_expr = NULL;
85 fr_value_box_list_t res;
86
87 tmpl_rules_t const parse_rules = {
88 .attr = {
89 .dict_def = request->local_dict,
90 .list_def = request_attr_request,
91 },
92 .xlat = {
93 .runtime_el = unlang_interpret_event_list(request),
94 },
95 .at_runtime = true,
96 };
97
98 count = fr_ldap_entry_value_find(&value_bv, handle, entry,
99 profile_ctx->inst->profile.fallthrough_attr);
100 if (count <= 0) return;
101 if (count > 1) {
102 RWARN("%s returned more than 1 value. Only evaluating the first.",
103 profile_ctx->inst->profile.fallthrough_attr);
104 }
105 value = fr_ldap_berval_to_string(request, &value_bv);
106
107 RDEBUG3("Parsing fallthrough condition %s", value);
108 if (xlat_tokenize_expression(request, &cond_expr,
110 NULL, &parse_rules) < 0) {
111 RPEDEBUG("Failed parsing '%s' value \"%s\"", profile_ctx->inst->profile.fallthrough_attr, value);
112 goto free;
113 }
114
115 if (xlat_impure_func(cond_expr)) {
116 fr_strerror_const("Fallthrough expression cannot depend on functions which call external databases");
117 goto free;
118 }
119
120 RDEBUG2("Checking fallthrough condition %s", value);
121 fr_value_box_list_init(&res);
122 if (unlang_xlat_eval(request, &res, request, cond_expr) < 0) {
123 RPEDEBUG("Failed evaluating condition");
124 goto free;
125 }
126 *fallthrough = (fr_value_box_list_head(&res) && fr_value_box_is_truthy(fr_value_box_list_head(&res))) ? true : false;
127 fr_value_box_list_talloc_free(&res);
128 RDEBUG2("Fallthrough condition evaluated to %s", *fallthrough ? "true" : "false");
129 free:
131 talloc_free(cond_expr);
132 }
133}
134
135/** Process the results of a profile lookup
136 *
137 */
139{
140 ldap_profile_ctx_t *profile_ctx = talloc_get_type_abort(uctx, ldap_profile_ctx_t);
141 fr_ldap_query_t *query = profile_ctx->query;
142 LDAP *handle;
143 LDAPMessage *entry;
144 int ldap_errno;
145 char *dn = NULL;
146 bool fallthrough = true;
147
148 /*
149 * Tell the caller what happened
150 */
151 if (profile_ctx->ret) *profile_ctx->ret = query->ret;
152
153 switch (query->ret) {
155 break;
156
159 if (profile_ctx->dn) {
160 RDEBUG2("Profile object \"%s\" not found", profile_ctx->dn);
161 } else {
162 RDEBUG2("No profile objects found");
163 }
164 goto next;
165
166 default:
167 goto finish;
168 }
169
170 fr_assert(query->result);
171 handle = query->ldap_conn->handle;
172
173 entry = ldap_first_entry(handle, query->result);
174 if (!entry) {
175 ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
176 REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno));
177 if (profile_ctx->ret) *profile_ctx->ret = LDAP_RESULT_NO_RESULT;
178 goto finish;
179 }
180
181 RDEBUG2("Processing profile attributes");
182 RINDENT();
183 while (entry) {
184 if (RDEBUG_ENABLED2) {
185 dn = ldap_get_dn(handle, entry);
186 RDEBUG2("Processing \"%s\"", dn);
187 ldap_memfree(dn);
188 }
189
190 RINDENT();
191 ldap_profile_entry_map(&fallthrough, request, profile_ctx, handle, entry);
192 REXDENT();
193 if (!fallthrough) break;
194
195 entry = ldap_next_entry(handle, entry);
196 }
197 REXDENT();
198
199 if (!fallthrough) goto finish;
200
201next:
202 /*
203 * Chain the search for the next profile (search_mode = seq)
204 */
205 while (profile_ctx->next && *profile_ctx->next) {
206 LDAPControl *serverctrls[] = { profile_ctx->inst->profile.obj_sort_ctrl, NULL };
207
208 TALLOC_FREE(profile_ctx->query);
209
210 profile_ctx->dn = *profile_ctx->next++;
211
213 talloc_free(profile_ctx);
214 return UNLANG_ACTION_FAIL;
215 }
216 return fr_ldap_trunk_search(profile_ctx, &profile_ctx->query, request, profile_ctx->ttrunk,
217 profile_ctx->dn, profile_ctx->inst->profile.obj_scope,
218 profile_ctx->filter, profile_ctx->expanded->attrs, serverctrls, NULL);
219 }
220
221finish:
222 talloc_free(profile_ctx);
224}
225
226/** Push the resume frame and start the first profile search
227 *
228 * Cancellation is handled by the frame fr_ldap_trunk_search pushes above
229 * this one, which abandons the in-flight query and detaches it from the
230 * trunk request before the unwind frees profile_ctx.
231 */
233 char const *base, int scope, char const *filter)
234{
235 LDAPControl *serverctrls[] = { profile_ctx->inst->profile.obj_sort_ctrl, NULL };
236
237 if (unlang_function_push(request,
238 NULL,
240 NULL, 0,
242 profile_ctx) < 0) {
243 talloc_free(profile_ctx);
244 return UNLANG_ACTION_FAIL;
245 }
246
247 return fr_ldap_trunk_search(profile_ctx, &profile_ctx->query, request, profile_ctx->ttrunk,
248 base, scope, filter,
249 profile_ctx->expanded->attrs, serverctrls, NULL);
250}
251
252/** Search for and apply an LDAP profile
253 *
254 * LDAP profiles are mapped using the same attribute map as user objects, they're used to add common
255 * sets of attributes to the request.
256 *
257 * @param[out] ret Where to write the result of the query.
258 * @param[out] applied Where to write the number of profiles applied.
259 * @param[in] inst LDAP module instance.
260 * @param[in] request Current request.
261 * @param[in] ttrunk Trunk connection on which to run LDAP queries.
262 * @param[in] dn of profile object to apply.
263 * @param[in] scope to apply when looking up profiles.
264 * @param[in] filter to apply when looking up profiles.
265 * @param[in] expanded Structure containing a list of xlat
266 * expanded attribute names and mapping information.
267 * @return One of the RLM_MODULE_* values.
268 */
270 rlm_ldap_t const *inst, request_t *request, fr_ldap_thread_trunk_t *ttrunk,
271 char const *dn, int scope, char const *filter, fr_ldap_map_exp_t const *expanded)
272{
273 ldap_profile_ctx_t *profile_ctx;
274
275 if (!dn || !*dn) return UNLANG_ACTION_CALCULATE_RESULT;
276
277 MEM(profile_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ldap_profile_ctx_t));
278 *profile_ctx = (ldap_profile_ctx_t) {
279 .ret = ret,
280 .applied = applied,
281 .dn = dn,
282 .expanded = expanded,
283 .inst = inst,
284 .ttrunk = ttrunk
285 };
286 if (ret) *ret = LDAP_RESULT_ERROR;
287
288 return ldap_profile_search_push(profile_ctx, request, dn, scope, filter);
289}
290
291/** Search for and apply a set of LDAP profiles
292 *
293 * With search_mode = bulk, a single search retrieves every profile object, matching
294 * entries by DN using the attribute configured (or detected) for the directory,
295 * and profiles are applied in result order.
296 * With search_mode = seq, one search is run per profile DN, applied in list order.
297 *
298 * @param[out] ret Where to write the result of the last query.
299 * @param[out] applied Incremented by the number of profile maps applied.
300 * @param[in] inst LDAP module instance.
301 * @param[in] request Current request.
302 * @param[in] ttrunk Trunk connection on which to run LDAP queries.
303 * @param[in] dn_list NULL terminated list of profile object DNs to apply,
304 * in application order (default profile first).
305 * Must contain at least one DN, and no empty strings.
306 * @param[in] filter to apply when looking up profiles.
307 * @param[in] expanded Structure containing a list of xlat
308 * expanded attribute names and mapping information.
309 * @return An unlang_action_t.
310 */
312 rlm_ldap_t const *inst, request_t *request, fr_ldap_thread_trunk_t *ttrunk,
313 char const * const *dn_list, char const *filter,
314 fr_ldap_map_exp_t const *expanded)
315{
316 ldap_profile_ctx_t *profile_ctx;
317
318 fr_assert(dn_list && *dn_list);
319
320 MEM(profile_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ldap_profile_ctx_t));
321 *profile_ctx = (ldap_profile_ctx_t) {
322 .ret = ret,
323 .applied = applied,
324 .expanded = expanded,
325 .inst = inst,
326 .ttrunk = ttrunk,
327 .filter = filter
328 };
329 if (ret) *ret = LDAP_RESULT_ERROR;
330
331 switch (inst->profile.search_mode) {
333 {
334 char const *base, *dn_attr;
335
336 dn_attr = inst->dn_attr;
337 if (!dn_attr) dn_attr = ttrunk->directory->dn_attr;
338 fr_assert(dn_attr);
339
340 base = fr_ldap_directory_common_base_find(ttrunk->directory, dn_list);
341 if (!base) {
342 RWDEBUG("Retrieving profiles one at a time, no naming context contains every profile DN");
343 goto seq;
344 }
345
346 return ldap_profile_search_push(profile_ctx, request, base, LDAP_SCOPE_SUB,
347 fr_ldap_filter_afrom_dn_list(profile_ctx, dn_attr, filter, dn_list));
348 }
349
351 seq:
352 profile_ctx->dn = dn_list[0];
353 profile_ctx->next = dn_list + 1;
354
355 return ldap_profile_search_push(profile_ctx, request, profile_ctx->dn, inst->profile.obj_scope, filter);
356
358 fr_assert_msg(false, "search mode auto should've been resolved at startup");
359 break;
360 }
361
362 talloc_free(profile_ctx);
363 return UNLANG_ACTION_FAIL;
364}
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition action.h:36
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
#define USES_APPLE_DEPRECATED_API
Definition build.h:499
#define RCSID(id)
Definition build.h:512
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:202
#define MEM(x)
Definition debug.h:36
Test enumeration values.
Definition dict_test.h:92
#define unlang_function_repeat_set(_request, _repeat)
Set a new repeat function for an existing function frame.
Definition function.h:108
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition function.h:179
free(array)
talloc_free(hp)
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:2042
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2418
#define UNLANG_SUB_FRAME
Definition interpret.h:37
char const * fr_ldap_directory_common_base_find(fr_ldap_directory_t const *directory, char const *const *dn_list)
Find the naming context which contains a set of DNs.
Definition directory.c:296
int fr_ldap_map_do(request_t *request, char const *check_attr, char const *valuepair_attr, fr_ldap_map_exp_t const *expanded, LDAPMessage *entry)
Convert attribute map into valuepairs.
Definition map.c:377
int fr_ldap_entry_value_find(struct berval *out, LDAP *handle, LDAPMessage *entry, char const *attr)
Find an attribute in an entry, returning its first value referenced in place.
Definition util.c:748
LDAP * handle
libldap handle.
Definition base.h:343
fr_ldap_result_code_t ret
Result code.
Definition base.h:473
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition util.c:778
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition base.h:460
fr_ldap_result_code_t
LDAP query result codes.
Definition base.h:189
@ LDAP_RESULT_ERROR
A general error occurred.
Definition base.h:192
@ LDAP_RESULT_SUCCESS
Successfully got LDAP results.
Definition base.h:191
@ LDAP_RESULT_NO_RESULT
No results returned.
Definition base.h:195
@ LDAP_RESULT_BAD_DN
The requested DN does not exist.
Definition base.h:194
LDAPMessage * result
Head of LDAP results list.
Definition base.h:471
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition base.h:407
char const * attrs[LDAP_MAX_ATTRMAP+LDAP_MAP_RESERVED+1]
Reserve some space for access attributes.
Definition base.h:373
char const * dn_attr
Attribute to match an entry's DN in a search filter.
Definition base.h:214
char * fr_ldap_filter_afrom_dn_list(TALLOC_CTX *ctx, char const *dn_attr, char const *filter, char const *const *dn_list)
Build a filter matching a set of objects by DN.
Definition util.c:909
Result of expanding the RHS of a set of maps.
Definition base.h:371
LDAP query structure.
Definition base.h:425
Thread LDAP trunk structure.
Definition base.h:402
unlang_action_t fr_ldap_trunk_search(TALLOC_CTX *ctx, fr_ldap_query_t **out, request_t *request, fr_ldap_thread_trunk_t *ttrunk, char const *base_dn, int scope, char const *filter, char const *const *attrs, LDAPControl **serverctrls, LDAPControl **clientctrls)
Run an async search LDAP query on a trunk connection.
Definition base.c:718
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:455
#define RWDEBUG(fmt,...)
Definition log.h:373
#define RDEBUG3(fmt,...)
Definition log.h:355
#define RWARN(fmt,...)
Definition log.h:309
#define RPEDEBUG(fmt,...)
Definition log.h:388
#define RINDENT()
Indent R* messages by one level.
Definition log.h:442
unlang_action_t rlm_ldap_map_profiles(fr_ldap_result_code_t *ret, int *applied, rlm_ldap_t const *inst, request_t *request, fr_ldap_thread_trunk_t *ttrunk, char const *const *dn_list, char const *filter, fr_ldap_map_exp_t const *expanded)
Search for and apply a set of LDAP profiles.
Definition profile.c:311
int * applied
Number of profiles applied.
Definition profile.c:45
fr_ldap_thread_trunk_t * ttrunk
Trunk used for profile searches.
Definition profile.c:51
char const * filter
Filter to apply to profile searches.
Definition profile.c:52
unlang_action_t rlm_ldap_map_profile(fr_ldap_result_code_t *ret, int *applied, rlm_ldap_t const *inst, request_t *request, fr_ldap_thread_trunk_t *ttrunk, char const *dn, int scope, char const *filter, fr_ldap_map_exp_t const *expanded)
Search for and apply an LDAP profile.
Definition profile.c:269
fr_ldap_result_code_t * ret
Result of the query and applying the map.
Definition profile.c:44
rlm_ldap_t const * inst
Definition profile.c:49
static void ldap_profile_entry_map(bool *fallthrough, request_t *request, ldap_profile_ctx_t *profile_ctx, LDAP *handle, LDAPMessage *entry)
Apply the attribute map to a single profile entry.
Definition profile.c:64
char const * dn
DN of the profile object being retrieved.
Definition profile.c:47
fr_ldap_map_exp_t const * expanded
Definition profile.c:50
static unlang_action_t ldap_profile_search_push(ldap_profile_ctx_t *profile_ctx, request_t *request, char const *base, int scope, char const *filter)
Push the resume frame and start the first profile search.
Definition profile.c:232
fr_ldap_query_t * query
Definition profile.c:46
static unlang_action_t ldap_map_profile_resume(request_t *request, void *uctx)
Process the results of a profile lookup.
Definition profile.c:138
char const *const * next
Next profile DN to search for (search_mode = seq).
Definition profile.c:53
Holds state of in progress async profile lookups.
Definition profile.c:43
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG_ENABLED2()
#define RDEBUG2(fmt,...)
fr_dict_attr_t const * request_attr_request
Definition request.c:43
@ LDAP_PROFILE_SEARCH_MODE_AUTO
Resolved at instantiation, LDAP_PROFILE_SEARCH_MODE_BULK when server side sorting is configured and t...
Definition rlm_ldap.h:24
@ LDAP_PROFILE_SEARCH_MODE_BULK
A single search retrieving every profile object, applied in result order.
Definition rlm_ldap.h:28
@ LDAP_PROFILE_SEARCH_MODE_SEQ
One search per profile DN, applied in list order.
Definition rlm_ldap.h:27
struct rlm_ldap_t::@178 profile
char const * valuepair_attr
Generic dynamic mapping attribute, contains a RADIUS attribute and value.
Definition rlm_ldap.h:120
#define FR_SBUFF_IN(_start, _len_or_end)
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:339
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
return count
Definition module.c:155
eap_aka_sim_process_conf_t * inst
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
int unlang_xlat_eval(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, xlat_exp_head_t const *xlat)
Evaluate a "pure" (or not impure) xlat.
Definition xlat.c:711
bool xlat_impure_func(xlat_exp_head_t const *head)
fr_slen_t xlat_tokenize_expression(TALLOC_CTX *ctx, xlat_exp_head_t **head, fr_sbuff_t *in, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Definition xlat_expr.c:3163
#define fr_strerror_const(_msg)
Definition strerror.h:223
bool fr_value_box_is_truthy(fr_value_box_t const *in)
Check truthiness of values.
Definition value.c:7390