The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 2be5316bd858286318a4e4266dd6e7c3be9de668 $
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  */
27 RCSID("$Id: 2be5316bd858286318a4e4266dd6e7c3be9de668 $")
28 
30 
31 #include <freeradius-devel/util/debug.h>
32 
33 #define LOG_PREFIX mctx->inst->name
34 
35 #include "rlm_ldap.h"
36 
37 /** Holds state of user searches in progress
38  *
39  */
40 typedef struct {
41  rlm_ldap_t const *inst;
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  */
53 static unlang_action_t ldap_find_user_async_result(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
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;
60  fr_pair_t *vp;
61 
62  switch (query->ret) {
64  break;
65 
68 
69  default:
71  }
72 
73  cnt = ldap_count_entries(query->ldap_conn->handle, query->result);
74 
75  if ((!user_ctx->inst->user.obj_sort_ctrl) && (cnt > 1)) {
76  REDEBUG("Ambiguous search result, returned %i unsorted entries (should return 1 or 0). "
77  "Enable sorting, or specify a more restrictive base_dn, filter or scope", cnt);
78  REDEBUG("The following entries were returned:");
79  RINDENT();
80  for (entry = ldap_first_entry(query->ldap_conn->handle, query->result);
81  entry;
82  entry = ldap_next_entry(query->ldap_conn->handle, entry)) {
83  dn = ldap_get_dn(query->ldap_conn->handle, entry);
84  REDEBUG("%s", dn);
85  ldap_memfree(dn);
86  }
87  REXDENT();
89  }
90 
91  entry = ldap_first_entry(query->ldap_conn->handle, query->result);
92  if (!entry) {
93  ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
94  REDEBUG("Failed retrieving entry: %s",
95  ldap_err2string(ldap_errno));
96 
98  }
99 
100  dn = ldap_get_dn(query->ldap_conn->handle, entry);
101  if (!dn) {
102  ldap_get_option(query->ldap_conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno);
103  REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno));
104 
106  }
108 
109  RDEBUG2("User object found at DN \"%s\"", dn);
110 
112  fr_pair_value_strdup(vp, dn, false);
113  ldap_memfree(dn);
114  if (user_ctx->out) *user_ctx->out = user_ctx->query;
115 
117 }
118 
119 /** Cancel a user search
120  */
121 static void ldap_find_user_async_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
122 {
123  ldap_user_find_ctx_t *user_ctx = talloc_get_type_abort(uctx, ldap_user_find_ctx_t);
124 
125  /*
126  * If the query is not in flight, just return
127  */
128  if (!user_ctx->query || !user_ctx->query->treq) return;
129 
131 }
132 
133 /** Initiate asynchronous retrieval of the DN of a user object
134  *
135  * Retrieves the DN of a user and adds it to the control list as LDAP-UserDN.
136  * Will also retrieve any attributes passed.
137  *
138  * This potentially allows for all authorization and authentication checks to be performed in one
139  * ldap search operation, which is a big bonus given the number of crappy, slow *cough*AD*cough*
140  * LDAP directory servers out there.
141  *
142  * @param[in] ctx in which to allocate the query.
143  * @param[in] inst rlm_ldap configuration.
144  * @param[in] request Current request.
145  * @param[in] base DN to search in.
146  * @param[in] filter to use in LDAP search.
147  * @param[in] ttrunk LDAP thread trunk to use.
148  * @param[in] attrs Additional attributes to retrieve, may be NULL.
149  * @param[in] query_out Where to put a pointer to the LDAP query structure -
150  * for extracting extra returned attributes, may be NULL.
151  * @return
152  * - UNLANG_ACTION_PUSHED_CHILD on success.
153  * - UNLANG_ACTION_FAIL on failure.
154  */
156  fr_value_box_t *base, fr_value_box_t *filter,
157  fr_ldap_thread_trunk_t *ttrunk, char const *attrs[], fr_ldap_query_t **query_out)
158 {
159  static char const *tmp_attrs[] = { NULL };
160  ldap_user_find_ctx_t *user_ctx;
161  LDAPControl *serverctrls[] = { inst->user.obj_sort_ctrl, NULL };
162 
163  if (!attrs) memset(&attrs, 0, sizeof(tmp_attrs));
164 
165  user_ctx = talloc_zero(ctx, ldap_user_find_ctx_t);
166  *user_ctx = (ldap_user_find_ctx_t) {
167  .inst = inst,
168  .ttrunk = ttrunk,
169  .base_dn = base->vb_strvalue,
170  .attrs = attrs,
171  .out = query_out
172  };
173 
174  if (filter) user_ctx->filter = filter->vb_strvalue;
176  ~FR_SIGNAL_CANCEL, UNLANG_SUB_FRAME, user_ctx) < 0) {
177  talloc_free(user_ctx);
178  return UNLANG_ACTION_FAIL;
179  }
180 
181  return fr_ldap_trunk_search(user_ctx, &user_ctx->query, request, user_ctx->ttrunk,
182  user_ctx->base_dn, user_ctx->inst->user.obj_scope, user_ctx->filter,
183  user_ctx->attrs, serverctrls, NULL);
184 }
185 
186 /** Check for presence of access attribute in result
187  *
188  * @param[in] inst rlm_ldap configuration.
189  * @param[in] request Current request.
190  * @param[in] entry retrieved by rlm_ldap_find_user or fr_ldap_search.
191  * @return
192  * - #RLM_MODULE_DISALLOW if the user was denied access.
193  * - #RLM_MODULE_OK otherwise.
194  */
196 {
198  struct berval **values = NULL;
199 
200  values = ldap_get_values_len(fr_ldap_handle_thread_local(), entry, inst->user.obj_access_attr);
201  if (values) {
202  size_t negate_value_len = talloc_array_length(inst->user.access_value_negate) - 1;
203  if (inst->user.access_positive) {
204  if ((values[0]->bv_len >= negate_value_len) &&
205  (strncasecmp(values[0]->bv_val, inst->user.access_value_negate, negate_value_len) == 0)) {
206  REDEBUG("\"%s\" attribute exists but is set to '%s' - user locked out",
207  inst->user.obj_access_attr, inst->user.access_value_negate);
209  goto done;
210  }
211  /* RLM_MODULE_OK set above... */
212  } else if ((values[0]->bv_len < negate_value_len) ||
213  (strncasecmp(values[0]->bv_val, inst->user.access_value_negate, negate_value_len) != 0)) {
214  REDEBUG("\"%s\" attribute exists - user locked out", inst->user.obj_access_attr);
216  goto done;
217  }
218  {
219  size_t suspend_value_len = talloc_array_length(inst->user.access_value_suspend) - 1;
220  if ((values[0]->bv_len == suspend_value_len) &&
221  (strncasecmp(values[0]->bv_val, inst->user.access_value_suspend, suspend_value_len) == 0)) {
222  RIDEBUG("\"%s\" attribute exists and indicates suspension", inst->user.obj_access_attr);
223  ret = LDAP_ACCESS_SUSPENDED;
224  goto done;
225  }
226  }
227  done:
228  ldap_value_free_len(values);
229  } else if (inst->user.access_positive) {
230  REDEBUG("No \"%s\" attribute - user locked out", inst->user.obj_access_attr);
232  }
233 
234  return ret;
235 }
236 
237 /** Verify we got a password from the search
238  *
239  * Checks to see if after the LDAP to RADIUS mapping has been completed that a reference password.
240  *
241  * @param[in] request Current request.
242  * @param[in] inst Current LDAP instance.
243  * @param[in] inst_name Name of LDAP module instance for debug messages.
244  * @param[in] expect_password Whether we should be expecting a password.
245  * @param[in] ttrunk the connection thread trunk.
246  */
247 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)
248 {
249  fr_pair_t *parent;
250 
251  /*
252  * More warning messages for people who can't be bothered to read the documentation.
253  *
254  * Expect_password is set when we process the mapping, and is only true if there was a mapping between
255  * an LDAP attribute and a password reference attribute in the control list.
256  */
257  if ((inst->user.expect_password_is_set && !inst->user.expect_password) || !expect_password || !RDEBUG_ENABLED2) return;
258 
259  parent = fr_pair_find_by_da_nested(&request->control_pairs, NULL, attr_password);
260  if (!parent) parent = request->control_ctx;
261 
262  if (!fr_pair_find_by_da_nested(&parent->vp_group, NULL, attr_cleartext_password) &&
263  !fr_pair_find_by_da_nested(&parent->vp_group, NULL, attr_nt_password) &&
266  switch (ttrunk->directory->type) {
268  RWDEBUG2("!!! Found map between LDAP attribute and a FreeRADIUS password attribute");
269  RWDEBUG2("!!! Active Directory does not allow passwords to be read via LDAP");
270  RWDEBUG2("!!! Remove the password map and either:");
271  RWDEBUG2("!!! - Configure authentication via ntlm_auth (mschapv2 only)");
272  RWDEBUG2("!!! - Configure authentication via wbclient (mschapv2 only)");
273  RWDEBUG2("!!! - Bind as the user by listing %s in the authenticate section, and",
274  inst_name);
275  RWDEBUG2("!!! setting attribute &control.Auth-Type := '%s' in the authorize section",
276  inst_name);
277  RWDEBUG2("!!! (pap only)");
278 
279  break;
280 
282  RWDEBUG2("!!! Found map between LDAP attribute and a FreeRADIUS password attribute");
283  RWDEBUG2("!!! eDirectory does not allow passwords to be retrieved via LDAP search");
284  RWDEBUG2("!!! Remove the password map and either:");
285  RWDEBUG2("!!! - Set 'edir = yes' and enable the universal password feature on your");
286  RWDEBUG2("!!! eDir server (recommended)");
287  RWDEBUG2("!!! - Bind as the user by listing %s in the authenticate section, and",
288  inst_name);
289  RWDEBUG2("!!! setting attribute &control.Auth-Type := '%s' in the authorize section",
290  inst_name);
291  RWDEBUG("!!! (pap only)");
292  break;
293 
294  default:
295  RWDEBUG2("!!! Found map between LDAP attribute and a FreeRADIUS password attribute");
296  RWDEBUG2("!!! but no password attribute found in search result");
297  RWDEBUG2("!!! Either:");
298  RWDEBUG2("!!! - Ensure the user object contains a password attribute, and that");
299  RWDEBUG2("!!! %c%s%c has permission to read that password attribute (recommended)",
300  ttrunk->config.admin_identity ? '"' : '\0',
301  ttrunk->config.admin_identity ? ttrunk->config.admin_identity : "the bind user",
302  ttrunk->config.admin_identity ? '"' : '\0');
303  RWDEBUG2("!!! - Bind as the user by listing %s in the authenticate section, and",
304  inst_name);
305  RWDEBUG2("!!! setting attribute &control.Auth-Type := '%s' in the authorize section",
306  inst_name);
307  RWDEBUG2("!!! (pap only)");
308  break;
309  }
310  }
311 }
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:431
#define RCSID(id)
Definition: build.h:444
#define UNUSED
Definition: build.h:313
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition: function.h:111
#define UNLANG_SUB_FRAME
Definition: interpret.h:36
size_t fr_ldap_util_normalise_dn(char *out, char const *in)
Normalise escape sequences in a DN.
Definition: util.c:439
fr_trunk_request_t * treq
Trunk request this query is associated with.
Definition: base.h:451
@ 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
LDAP * handle
libldap handle.
Definition: base.h:331
char const * admin_identity
Identity we bind as when we need to query the LDAP directory.
Definition: base.h:227
fr_ldap_result_code_t ret
Result code.
Definition: base.h:465
fr_ldap_config_t config
Config used for this connection.
Definition: base.h:401
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition: base.h:452
@ LDAP_RESULT_SUCCESS
Successfully got LDAP results.
Definition: base.h:188
@ LDAP_RESULT_NO_RESULT
No results returned.
Definition: base.h:192
LDAPMessage * result
Head of LDAP results list.
Definition: base.h:463
fr_ldap_directory_t * directory
The type of directory we're connected to.
Definition: base.h:402
fr_ldap_directory_type_t type
Canonical server implementation.
Definition: base.h:204
LDAP query structure.
Definition: base.h:420
Thread LDAP trunk structure.
Definition: base.h:397
LDAP * fr_ldap_handle_thread_local(void)
Get a thread local dummy LDAP handle.
Definition: base.c:1051
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:694
#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:2631
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition: pair.c:765
static fr_dict_attr_t const * attr_cleartext_password
Definition: radclient-ng.c:113
static bool done
Definition: radclient.c:80
#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_MODULE_INVALID
Definition: rcode.h:59
#define RETURN_MODULE_OK
Definition: rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
#define RETURN_MODULE_NOTFOUND
Definition: rcode.h:61
fr_dict_attr_t const * attr_nt_password
Definition: rlm_ldap.c:315
fr_dict_attr_t const * attr_ldap_userdn
Definition: rlm_ldap.c:314
fr_dict_attr_t const * attr_crypt_password
Definition: rlm_ldap.c:313
fr_dict_attr_t const * attr_password
Definition: rlm_ldap.c:311
fr_dict_attr_t const * attr_password_with_header
Definition: rlm_ldap.c:316
LDAP authorization and authentication module headers.
ldap_access_state_t
User's access state.
Definition: rlm_ldap.h:182
@ LDAP_ACCESS_SUSPENDED
User account has been suspended.
Definition: rlm_ldap.h:185
@ LDAP_ACCESS_ALLOWED
User is allowed to login.
Definition: rlm_ldap.h:183
@ LDAP_ACCESS_DISALLOWED
User it not allow to login (disabled)
Definition: rlm_ldap.h:184
struct rlm_ldap_t::@151 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
Definition: signal.h:48
RETURN_MODULE_FAIL
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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 fr_trunk_request_signal_cancel(fr_trunk_request_t *treq)
Cancel a trunk request.
Definition: trunk.c:2047
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, 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:155
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:121
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:247
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
static unlang_action_t ldap_find_user_async_result(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Process the results of an async user lookup.
Definition: user.c:53
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:195
Holds state of user searches in progress.
Definition: user.c:40
static fr_slen_t parent
Definition: pair.h:844