The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
user.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: a8798682e9b58040eda48ece21a4d69ca0c6fb2b $
19 * @file groups.c
20 * @brief LDAP module group functions.
21 *
22 * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 *
24 * @copyright 2013 Network RADIUS SAS (legal@networkradius.com)
25 * @copyright 2013-2015 The FreeRADIUS Server Project.
26 */
27RCSID("$Id: a8798682e9b58040eda48ece21a4d69ca0c6fb2b $")
28
30
31#include <freeradius-devel/util/debug.h>
32
33#define LOG_PREFIX mctx->mi->name
34
35#include "rlm_ldap.h"
36
37/** Holds state of user searches in progress
38 *
39 */
40typedef struct {
43 char const *base_dn;
44 char const *filter;
45 char const * const *attrs;
49
50/** Process the results of an async user lookup
51 *
52 */
54{
55 ldap_user_find_ctx_t *user_ctx = talloc_get_type_abort(uctx, ldap_user_find_ctx_t);
56 fr_ldap_query_t *query = user_ctx->query;
57 LDAPMessage *entry;
58 int cnt, ldap_errno;
59 char *dn;
61
62 /*
63 * Make the result available to the query
64 */
65 if (user_ctx->out) *user_ctx->out = user_ctx->query;
66
67 switch (query->ret) {
69 break;
70
72 /*
73 * DNs can now be dynamic, so a BAD DN often means the same thing as an empty result
74 */
77
78 default:
80 }
81
82 cnt = ldap_count_entries(query->ldap_conn->handle, query->result);
83
84 if ((!user_ctx->inst->user.obj_sort_ctrl) && (cnt > 1)) {
85 REDEBUG("Ambiguous search result, returned %i unsorted entries (should return 1 or 0). "
86 "Enable sorting, or specify a more restrictive base_dn, filter or scope", cnt);
87 REDEBUG("The following entries were returned:");
88 RINDENT();
89 for (entry = ldap_first_entry(query->ldap_conn->handle, query->result);
90 entry;
91 entry = ldap_next_entry(query->ldap_conn->handle, entry)) {
92 dn = ldap_get_dn(query->ldap_conn->handle, entry);
93 REDEBUG("%s", dn);
94 ldap_memfree(dn);
95 }
96 REXDENT();
98 }
99
100 entry = ldap_first_entry(query->ldap_conn->handle, query->result);
101 if (!entry) {
102 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
103 REDEBUG("Failed retrieving entry: %s",
104 ldap_err2string(ldap_errno));
105
107 }
108
109 dn = ldap_get_dn(query->ldap_conn->handle, entry);
110 if (!dn) {
111 ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
112 REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));
113
115 }
117
118 RDEBUG2("User object found at DN \"%s\"", dn);
119
121 fr_pair_value_strdup(vp, dn, false);
122 ldap_memfree(dn);
123
125}
126
127/** Cancel a user search
128 */
129static void ldap_find_user_async_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
130{
131 ldap_user_find_ctx_t *user_ctx = talloc_get_type_abort(uctx, ldap_user_find_ctx_t);
132
133 /*
134 * If the query is not in flight, just return
135 */
136 if (!user_ctx->query || !user_ctx->query->treq) return;
137
139}
140
141/** Initiate asynchronous retrieval of the DN of a user object
142 *
143 * Retrieves the DN of a user and adds it to the control list as LDAP-UserDN.
144 * Will also retrieve any attributes passed.
145 *
146 * This potentially allows for all authorization and authentication checks to be performed in one
147 * ldap search operation, which is a big bonus given the number of crappy, slow *cough*AD*cough*
148 * LDAP directory servers out there.
149 *
150 * @param[in] ctx in which to allocate the query.
151 * @param[in] p_result Where rcode from running the query will be written.
152 * @param[in] inst rlm_ldap configuration.
153 * @param[in] request Current request.
154 * @param[in] base DN to search in.
155 * @param[in] filter to use in LDAP search.
156 * @param[in] ttrunk LDAP thread trunk to use.
157 * @param[in] attrs Additional attributes to retrieve, may be NULL.
158 * @param[in] query_out Where to put a pointer to the LDAP query structure -
159 * for extracting extra returned attributes, may be NULL.
160 * @return
161 * - UNLANG_ACTION_PUSHED_CHILD on success.
162 * - UNLANG_ACTION_FAIL on failure.
163 */
165 unlang_result_t *p_result,
166 rlm_ldap_t const *inst, request_t *request,
167 fr_value_box_t *base, fr_value_box_t *filter,
168 fr_ldap_thread_trunk_t *ttrunk, char const *attrs[], fr_ldap_query_t **query_out)
169{
170 static char const *tmp_attrs[] = { NULL };
171 ldap_user_find_ctx_t *user_ctx;
172 LDAPControl *serverctrls[] = { inst->user.obj_sort_ctrl, NULL };
173
174 if (!attrs) memset(&attrs, 0, sizeof(tmp_attrs));
175
176 user_ctx = talloc_zero(ctx, ldap_user_find_ctx_t);
177 *user_ctx = (ldap_user_find_ctx_t) {
178 .inst = inst,
179 .ttrunk = ttrunk,
180 .base_dn = base->vb_strvalue,
181 .attrs = attrs,
182 .out = query_out
183 };
184
185 if (filter) user_ctx->filter = filter->vb_strvalue;
186 if (unlang_function_push_with_result(/* ldap_find_user_async_result sets an rcode based on the search result */ p_result,
187 request,
188 NULL,
191 UNLANG_SUB_FRAME, user_ctx) < 0) {
192 talloc_free(user_ctx);
193 return UNLANG_ACTION_FAIL;
194 }
195
196 return fr_ldap_trunk_search(user_ctx, &user_ctx->query, request, user_ctx->ttrunk,
197 user_ctx->base_dn, user_ctx->inst->user.obj_scope, user_ctx->filter,
198 user_ctx->attrs, serverctrls, NULL);
199}
200
201/** Check for presence of access attribute in result
202 *
203 * @param[in] inst rlm_ldap configuration.
204 * @param[in] request Current request.
205 * @param[in] entry retrieved by rlm_ldap_find_user or fr_ldap_search.
206 * @return
207 * - #RLM_MODULE_DISALLOW if the user was denied access.
208 * - #RLM_MODULE_OK otherwise.
209 */
211{
213 struct berval **values = NULL;
214
215 values = ldap_get_values_len(fr_ldap_handle_thread_local(), entry, inst->user.obj_access_attr);
216 if (values) {
217 size_t negate_value_len = talloc_array_length(inst->user.access_value_negate) - 1;
218 if (inst->user.access_positive) {
219 if ((values[0]->bv_len >= negate_value_len) &&
220 (strncasecmp(values[0]->bv_val, inst->user.access_value_negate, negate_value_len) == 0)) {
221 REDEBUG("\"%s\" attribute exists but is set to '%s' - user locked out",
222 inst->user.obj_access_attr, inst->user.access_value_negate);
224 goto done;
225 }
226 /* RLM_MODULE_OK set above... */
227 } else if ((values[0]->bv_len < negate_value_len) ||
228 (strncasecmp(values[0]->bv_val, inst->user.access_value_negate, negate_value_len) != 0)) {
229 REDEBUG("\"%s\" attribute exists - user locked out", inst->user.obj_access_attr);
231 goto done;
232 }
233 {
234 size_t suspend_value_len = talloc_array_length(inst->user.access_value_suspend) - 1;
235 if ((values[0]->bv_len == suspend_value_len) &&
236 (strncasecmp(values[0]->bv_val, inst->user.access_value_suspend, suspend_value_len) == 0)) {
237 RIDEBUG("\"%s\" attribute exists and indicates suspension", inst->user.obj_access_attr);
239 goto done;
240 }
241 }
242 done:
243 ldap_value_free_len(values);
244 } else if (inst->user.access_positive) {
245 REDEBUG("No \"%s\" attribute - user locked out", inst->user.obj_access_attr);
247 }
248
249 return ret;
250}
251
252/** Verify we got a password from the search
253 *
254 * Checks to see if after the LDAP to RADIUS mapping has been completed that a reference password.
255 *
256 * @param[in] request Current request.
257 * @param[in] inst Current LDAP instance.
258 * @param[in] inst_name Name of LDAP module instance for debug messages.
259 * @param[in] expect_password Whether we should be expecting a password.
260 * @param[in] ttrunk the connection thread trunk.
261 */
262void rlm_ldap_check_reply(request_t *request, rlm_ldap_t const *inst, char const *inst_name, bool expect_password, fr_ldap_thread_trunk_t const *ttrunk)
263{
265
266 /*
267 * More warning messages for people who can't be bothered to read the documentation.
268 *
269 * Expect_password is set when we process the mapping, and is only true if there was a mapping between
270 * an LDAP attribute and a password reference attribute in the control list.
271 */
272 if ((inst->user.expect_password_is_set && !inst->user.expect_password) || !expect_password || !RDEBUG_ENABLED2) return;
273
274 parent = fr_pair_find_by_da(&request->control_pairs, NULL, attr_password);
275 if (!parent) goto warnings;
276
277 if (!fr_pair_find_by_da(&parent->vp_group, NULL, attr_cleartext_password) &&
278 !fr_pair_find_by_da(&parent->vp_group, NULL, attr_nt_password) &&
280 !fr_pair_find_by_da(&parent->vp_group, NULL, attr_crypt_password)) {
281 warnings:
282 switch (ttrunk->directory->type) {
285 RWDEBUG2("!!! Found map between LDAP attribute and a FreeRADIUS password attribute");
286 RWDEBUG2("!!! %s does not allow passwords to be read via LDAP",
287 (ttrunk->directory->type == FR_LDAP_DIRECTORY_SAMBA ? "Samba" : "Active Directory"));
288 RWDEBUG2("!!! Remove the password map and either:");
289 RWDEBUG2("!!! - Configure authentication via ntlm_auth (mschapv2 only)");
290 RWDEBUG2("!!! - Configure authentication via wbclient (mschapv2 only)");
291 RWDEBUG2("!!! - Bind as the user by listing %s in an 'authenticate %s' section, and",
292 inst_name, inst_name);
293 RWDEBUG2("!!! setting attribute control.Auth-Type := '%s' in the 'recv Access-Request' section",
294 inst_name);
295 RWDEBUG2("!!! (pap only)");
296
297 break;
298
300 RWDEBUG2("!!! Found map between LDAP attribute and a FreeRADIUS password attribute");
301 RWDEBUG2("!!! eDirectory does not allow passwords to be retrieved via LDAP search");
302 RWDEBUG2("!!! Remove the password map and either:");
303 RWDEBUG2("!!! - Set 'edir = yes' and enable the universal password feature on your");
304 RWDEBUG2("!!! eDir server (recommended)");
305 RWDEBUG2("!!! - Bind as the user by listing %s in an 'authenticate %s' section, and",
306 inst_name, inst_name);
307 RWDEBUG2("!!! setting attribute control.Auth-Type := '%s' in the 'recv Access-Request' section",
308 inst_name);
309 RWDEBUG("!!! (pap only)");
310 break;
311
312 default:
313 RWDEBUG2("!!! Found map between LDAP attribute and a FreeRADIUS password attribute");
314 RWDEBUG2("!!! but no password attribute found in search result");
315 RWDEBUG2("!!! Either:");
316 RWDEBUG2("!!! - Ensure the user object contains a password attribute, and that");
317 RWDEBUG2("!!! %c%s%c has permission to read that password attribute (recommended)",
318 ttrunk->config.admin_identity ? '"' : '\0',
319 ttrunk->config.admin_identity ? ttrunk->config.admin_identity : "the bind user",
320 ttrunk->config.admin_identity ? '"' : '\0');
321 RWDEBUG2("!!! - Bind as the user by listing %s in an 'authenticate %s' section, and",
322 inst_name, inst_name);
323 RWDEBUG2("!!! setting attribute control.Auth-Type := '%s' in the 'recv Access-Request' section",
324 inst_name);
325 RWDEBUG2("!!! (pap only)");
326 break;
327 }
328 }
329}
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
#define USES_APPLE_DEPRECATED_API
Definition build.h:472
#define RCSID(id)
Definition build.h:485
#define UNUSED
Definition build.h:317
#define MEM(x)
Definition debug.h:36
#define unlang_function_push_with_result(_result_p, _request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack that produces a result.
Definition function.h:144
#define UNLANG_SUB_FRAME
Definition interpret.h:37
size_t fr_ldap_util_normalise_dn(char *out, char const *in)
Normalise escape sequences in a DN.
Definition util.c:492
@ FR_LDAP_DIRECTORY_EDIRECTORY
Directory server is eDir.
Definition base.h:144
@ FR_LDAP_DIRECTORY_ACTIVE_DIRECTORY
Directory server is Active Directory.
Definition base.h:143
@ FR_LDAP_DIRECTORY_SAMBA
Directory server is Samba.
Definition base.h:154
LDAP * handle
libldap handle.
Definition base.h:333
char const * admin_identity
Identity we bind as when we need to query the LDAP directory.
Definition base.h:229
fr_ldap_result_code_t ret
Result code.
Definition base.h:472
trunk_request_t * treq
Trunk request this query is associated with.
Definition base.h:458
fr_ldap_config_t config
Config used for this connection.
Definition base.h:405
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition base.h:459
@ 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
fr_ldap_directory_type_t type
Canonical server implementation.
Definition base.h:206
LDAP query structure.
Definition base.h:424
Thread LDAP trunk structure.
Definition base.h:401
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
Definition base.c:1132
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:720
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:443
#define RWDEBUG(fmt,...)
Definition log.h:361
#define RWDEBUG2(fmt,...)
Definition log.h:362
#define RINDENT()
Indent R* messages by one level.
Definition log.h:430
talloc_free(reap)
int strncasecmp(char *s1, char *s2, int n)
Definition missing.c:36
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition pair.c:2640
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:700
static fr_dict_attr_t const * attr_cleartext_password
static bool done
Definition radclient.c:81
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG_ENABLED2()
Definition radclient.h:50
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define RIDEBUG(fmt,...)
Definition radsniff.h:65
#define RETURN_UNLANG_INVALID
Definition rcode.h:62
#define RETURN_UNLANG_NOTFOUND
Definition rcode.h:64
#define RETURN_UNLANG_FAIL
Definition rcode.h:59
#define RETURN_UNLANG_OK
Definition rcode.h:60
fr_dict_attr_t const * attr_nt_password
Definition rlm_ldap.c:329
fr_dict_attr_t const * attr_ldap_userdn
Definition rlm_ldap.c:328
fr_dict_attr_t const * attr_crypt_password
Definition rlm_ldap.c:327
fr_dict_attr_t const * attr_password
Definition rlm_ldap.c:325
fr_dict_attr_t const * attr_password_with_header
Definition rlm_ldap.c:330
LDAP authorization and authentication module headers.
ldap_access_state_t
User's access state.
Definition rlm_ldap.h:185
@ LDAP_ACCESS_SUSPENDED
User account has been suspended.
Definition rlm_ldap.h:188
@ LDAP_ACCESS_ALLOWED
User is allowed to login.
Definition rlm_ldap.h:186
@ LDAP_ACCESS_DISALLOWED
User it not allow to login (disabled)
Definition rlm_ldap.h:187
struct rlm_ldap_t::@173 user
#define pair_update_control(_attr, _da)
Return or allocate a fr_pair_t in the control list.
Definition pair.h:140
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
void trunk_request_signal_cancel(trunk_request_t *treq)
Cancel a trunk request.
Definition trunk.c:2168
static unlang_action_t ldap_find_user_async_result(unlang_result_t *p_result, request_t *request, void *uctx)
Process the results of an async user lookup.
Definition user.c:53
fr_ldap_thread_trunk_t * ttrunk
Definition user.c:42
fr_ldap_query_t ** out
Definition user.c:47
unlang_action_t rlm_ldap_find_user_async(TALLOC_CTX *ctx, unlang_result_t *p_result, rlm_ldap_t const *inst, request_t *request, fr_value_box_t *base, fr_value_box_t *filter, fr_ldap_thread_trunk_t *ttrunk, char const *attrs[], fr_ldap_query_t **query_out)
Initiate asynchronous retrieval of the DN of a user object.
Definition user.c:164
static void ldap_find_user_async_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Cancel a user search.
Definition user.c:129
char const * filter
Definition user.c:44
char const *const * attrs
Definition user.c:45
void rlm_ldap_check_reply(request_t *request, rlm_ldap_t const *inst, char const *inst_name, bool expect_password, fr_ldap_thread_trunk_t const *ttrunk)
Verify we got a password from the search.
Definition user.c:262
fr_ldap_query_t * query
Definition user.c:46
char const * base_dn
Definition user.c:43
rlm_ldap_t const * inst
Definition user.c:41
ldap_access_state_t rlm_ldap_check_access(rlm_ldap_t const *inst, request_t *request, LDAPMessage *entry)
Check for presence of access attribute in result.
Definition user.c:210
Holds state of user searches in progress.
Definition user.c:40
static fr_slen_t parent
Definition pair.h:841