The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
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: eee1453ee1658faf00ae1b40857d97ff20f16525 $
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: eee1453ee1658faf00ae1b40857d97ff20f16525 $")
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 = {
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 }
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 };
237
239 NULL,
241 NULL, FR_SIGNAL_NONE,
243 profile_ctx) < 0) {
244 talloc_free(profile_ctx);
245 return UNLANG_ACTION_FAIL;
246 }
247
248 ua = fr_ldap_trunk_search(profile_ctx, &profile_ctx->query, request, profile_ctx->ttrunk,
249 base, scope, filter,
250 profile_ctx->expanded->attrs, serverctrls, NULL);
251 if (ua == UNLANG_ACTION_FAIL) {
253 talloc_free(profile_ctx);
254 }
255
256 return ua;
257}
258
259/** Search for and apply an LDAP profile
260 *
261 * LDAP profiles are mapped using the same attribute map as user objects, they're used to add common
262 * sets of attributes to the request.
263 *
264 * @param[out] ret Where to write the result of the query.
265 * @param[out] applied Where to write the number of profiles applied.
266 * @param[in] inst LDAP module instance.
267 * @param[in] request Current request.
268 * @param[in] ttrunk Trunk connection on which to run LDAP queries.
269 * @param[in] dn of profile object to apply.
270 * @param[in] scope to apply when looking up profiles.
271 * @param[in] filter to apply when looking up profiles.
272 * @param[in] expanded Structure containing a list of xlat
273 * expanded attribute names and mapping information.
274 * @return One of the RLM_MODULE_* values.
275 */
278 char const *dn, int scope, char const *filter, fr_ldap_map_exp_t const *expanded)
279{
280 ldap_profile_ctx_t *profile_ctx;
281
282 if (!dn || !*dn) return UNLANG_ACTION_CALCULATE_RESULT;
283
285 *profile_ctx = (ldap_profile_ctx_t) {
286 .ret = ret,
287 .applied = applied,
288 .dn = dn,
289 .expanded = expanded,
290 .inst = inst,
291 .ttrunk = ttrunk
292 };
293 if (ret) *ret = LDAP_RESULT_ERROR;
294
295 return ldap_profile_search_push(profile_ctx, request, dn, scope, filter);
296}
297
298/** Search for and apply a set of LDAP profiles
299 *
300 * With search_mode = bulk, a single search retrieves every profile object, matching
301 * entries by DN using the attribute configured (or detected) for the directory,
302 * and profiles are applied in result order.
303 * With search_mode = seq, one search is run per profile DN, applied in list order.
304 *
305 * @param[out] ret Where to write the result of the last query.
306 * @param[out] applied Incremented by the number of profile maps applied.
307 * @param[in] inst LDAP module instance.
308 * @param[in] request Current request.
309 * @param[in] ttrunk Trunk connection on which to run LDAP queries.
310 * @param[in] dn_list NULL terminated list of profile object DNs to apply,
311 * in application order (default profile first).
312 * Must contain at least one DN, and no empty strings.
313 * @param[in] filter to apply when looking up profiles.
314 * @param[in] expanded Structure containing a list of xlat
315 * expanded attribute names and mapping information.
316 * @return An unlang_action_t.
317 */
320 char const * const *dn_list, char const *filter,
321 fr_ldap_map_exp_t const *expanded)
322{
323 ldap_profile_ctx_t *profile_ctx;
324
325 fr_assert(dn_list && *dn_list);
326
328 *profile_ctx = (ldap_profile_ctx_t) {
329 .ret = ret,
330 .applied = applied,
331 .expanded = expanded,
332 .inst = inst,
333 .ttrunk = ttrunk,
334 .filter = filter
335 };
336 if (ret) *ret = LDAP_RESULT_ERROR;
337
338 switch (inst->profile.search_mode) {
340 {
341 char const *base, *dn_attr;
342
343 dn_attr = inst->dn_attr;
344 if (!dn_attr) dn_attr = ttrunk->directory->dn_attr;
345 fr_assert(dn_attr);
346
347 base = fr_ldap_directory_common_base_find(ttrunk->directory, dn_list);
348 if (!base) {
349 RWDEBUG("Retrieving profiles one at a time, no naming context contains every profile DN");
350 goto seq;
351 }
352
353 return ldap_profile_search_push(profile_ctx, request, base, LDAP_SCOPE_SUB,
354 fr_ldap_filter_afrom_dn_list(profile_ctx, dn_attr, filter, dn_list));
355 }
356
358 seq:
359 profile_ctx->dn = dn_list[0];
360 profile_ctx->next = dn_list + 1;
361
362 return ldap_profile_search_push(profile_ctx, request, profile_ctx->dn, inst->profile.obj_scope, filter);
363
365 fr_assert_msg(false, "search mode auto should've been resolved at startup");
366 break;
367 }
368
369 talloc_free(profile_ctx);
370 return UNLANG_ACTION_FAIL;
371}
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:38
static uint8_t request[4]
#define USES_APPLE_DEPRECATED_API
Definition build.h:590
#define RCSID(id)
Definition build.h:603
#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:248
#define MEM(x)
Definition debug.h:38
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:110
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition function.h:181
free(array)
talloc_free(hp)
void unlang_interpet_frame_discard(request_t *request)
Discard the bottom most frame on the request's stack.
Definition interpret.c:2511
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:2053
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2538
#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:715
LDAP * handle
libldap handle.
Definition base.h:342
fr_ldap_result_code_t ret
Result code.
Definition base.h:472
char * fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in)
Convert a berval to a talloced string.
Definition util.c:745
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition base.h:459
fr_ldap_result_code_t
LDAP query result codes.
Definition base.h:188
@ LDAP_RESULT_ERROR
A general error occurred.
Definition base.h:191
@ LDAP_RESULT_SUCCESS
Successfully got LDAP results.
Definition base.h:190
@ LDAP_RESULT_NO_RESULT
No results returned.
Definition base.h:194
@ LDAP_RESULT_BAD_DN
The requested DN does not exist.
Definition base.h:193
LDAPMessage * result
Head of LDAP results list.
Definition base.h:470
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition base.h:406
char const * attrs[LDAP_MAX_ATTRMAP+LDAP_MAP_RESERVED+1]
Reserve some space for access attributes.
Definition base.h:372
char const * dn_attr
Attribute to match an entry's DN in a search filter.
Definition base.h:213
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:876
Result of expanding the RHS of a set of maps.
Definition base.h:370
LDAP query structure.
Definition base.h:424
Thread LDAP trunk structure.
Definition base.h:401
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:719
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:460
#define RWDEBUG(fmt,...)
Definition log.h:378
#define RDEBUG3(fmt,...)
Definition log.h:360
#define RWARN(fmt,...)
Definition log.h:314
#define RPEDEBUG(fmt,...)
Definition log.h:393
#define RINDENT()
Indent R* messages by one level.
Definition log.h:447
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:318
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:276
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
struct rlm_ldap_t::@204 profile
char const * valuepair_attr
Generic dynamic mapping attribute, contains a RADIUS attribute and value.
Definition rlm_ldap.h:120
@ 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
#define FR_SBUFF_IN(_start, _len_or_end)
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:346
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:343
@ FR_SIGNAL_NONE
No signals.
Definition signal.h:39
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:280
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:3224
#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:7627