The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
bind.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: ee3de78a0434a07b733bc1a5f9aa8228d54f3a06 $
19  * @file lib/ldap/bind.c
20  * @brief Asynchronous bind functions for LDAP.
21  *
22  * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23  */
24 RCSID("$Id: ee3de78a0434a07b733bc1a5f9aa8228d54f3a06 $")
25 
27 
28 #include <freeradius-devel/ldap/base.h>
29 #include <freeradius-devel/util/debug.h>
30 
31 /** Error reading from or writing to the file descriptor
32  *
33  * @param[in] el the event occurred in.
34  * @param[in] fd the event occurred on.
35  * @param[in] flags from kevent.
36  * @param[in] fd_errno The error that occurred.
37  * @param[in] uctx Connection config and handle.
38  */
40  UNUSED int flags, UNUSED int fd_errno, void *uctx)
41 {
42  fr_ldap_bind_ctx_t *bind_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_ctx_t);
43  fr_ldap_connection_t *c = bind_ctx->c;
44 
45  talloc_free(bind_ctx);
46  fr_ldap_state_error(c); /* Restart the connection state machine */
47 }
48 
49 /** Parse a bind response from a server
50  *
51  * @param[in] el the event occurred in.
52  * @param[in] fd the event occurred on.
53  * @param[in] flags from kevent.
54  * @param[in] uctx bind_ctx containing credentials, and connection config/handle.
55  */
56 static void _ldap_bind_io_read(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
57 {
58  fr_ldap_bind_ctx_t *bind_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_ctx_t);
59  fr_ldap_connection_t *c = bind_ctx->c;
60 
61  fr_ldap_rcode_t status;
62 
63  /*
64  * We're I/O driven, if there's no data someone lied to us
65  */
66  status = fr_ldap_result(NULL, NULL, c, bind_ctx->msgid, LDAP_MSG_ALL, bind_ctx->bind_dn, fr_time_delta_wrap(0));
67  switch (status) {
68  case LDAP_PROC_SUCCESS:
69  DEBUG2("Bind as \"%s\" to \"%s\" successful",
70  *bind_ctx->bind_dn ? bind_ctx->bind_dn : "(anonymous)", c->config->server);
71  fr_ldap_state_next(c); /* onto the next operation */
72  break;
73 
75  PERROR("Bind as \"%s\" to \"%s\" not permitted",
76  *bind_ctx->bind_dn ? bind_ctx->bind_dn : "(anonymous)", c->config->server);
77  fr_ldap_state_error(c); /* Restart the connection state machine */
78  return;
79 
80  default:
81  PERROR("Bind as \"%s\" to \"%s\" failed",
82  *bind_ctx->bind_dn ? bind_ctx->bind_dn : "(anonymous)", c->config->server);
83  fr_ldap_state_error(c); /* Restart the connection state machine */
84  return;
85  }
86  talloc_free(bind_ctx); /* Also removes fd events */
87 }
88 
89 /** Send a bind request to a server
90  *
91  * @param[in] el the event occurred in.
92  * @param[in] fd the event occurred on.
93  * @param[in] flags from kevent.
94  * @param[in] uctx bind_ctx containing credentials, and connection config/handle.
95  */
96 static void _ldap_bind_io_write(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
97 {
98  fr_ldap_bind_ctx_t *bind_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_ctx_t);
99  fr_ldap_connection_t *c = bind_ctx->c;
100 
101  LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS];
102  LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS];
103 
104  int ret;
105  struct berval cred;
106 
107  fr_ldap_control_merge(our_serverctrls, our_clientctrls,
108  NUM_ELEMENTS(our_serverctrls),
109  NUM_ELEMENTS(our_clientctrls),
110  c, bind_ctx->serverctrls, bind_ctx->clientctrls);
111 
112  if (bind_ctx->password) {
113  memcpy(&cred.bv_val, &bind_ctx->password, sizeof(cred.bv_val));
114  cred.bv_len = talloc_array_length(bind_ctx->password) - 1;
115  } else {
116  cred.bv_val = NULL;
117  cred.bv_len = 0;
118  }
119 
120  /*
121  * Yes, confusingly named. This is the simple version
122  * of the SASL bind function that should always be
123  * available.
124  */
125  ret = ldap_sasl_bind(c->handle, bind_ctx->bind_dn, LDAP_SASL_SIMPLE, &cred,
126  our_serverctrls, our_clientctrls, &bind_ctx->msgid);
127  switch (ret) {
128  /*
129  * If the handle was not connected, this operation
130  * can return either LDAP_X_CONNECTING or LDAP_SUCCESS
131  * depending on how fast the connection came up
132  * and whether it was connectionless.
133  */
134  case LDAP_X_CONNECTING: /* Connection in progress - retry later */
135  ret = ldap_get_option(c->handle, LDAP_OPT_DESC, &fd);
136  if (!fr_cond_assert(ret == LDAP_OPT_SUCCESS)) {
137  error:
138  talloc_free(bind_ctx);
140  fr_ldap_state_error(c); /* Restart the connection state machine */
141  return;
142  }
143 
144  ret = fr_event_fd_insert(bind_ctx, NULL, el, fd,
145  NULL,
146  _ldap_bind_io_write, /* We'll be called again when the conn is open */
148  bind_ctx);
149  if (!fr_cond_assert(ret == 0)) goto error;
150  break;
151 
152  case LDAP_SUCCESS:
153  if (fd < 0 ) {
154  ret = ldap_get_option(c->handle, LDAP_OPT_DESC, &fd);
155  if ((ret != LDAP_OPT_SUCCESS) || (fd < 0)) goto error;
156  }
157  c->fd = fd;
158  ret = fr_event_fd_insert(bind_ctx, NULL, el, fd,
160  NULL,
162  bind_ctx);
163  if (!fr_cond_assert(ret == 0)) goto error;
164  break;
165 
166  default:
167  ERROR("Bind failed: %s", ldap_err2string(ret));
168  goto error;
169  }
170 
172 }
173 
174 /** Install I/O handlers for the bind operation
175  *
176  * @param[in] c connection to StartTLS on.
177  * @param[in] bind_dn Identity to bind with.
178  * @param[in] password Password to bind with.
179  * @param[in] serverctrls Extra controls to pass to the server.
180  * @param[in] clientctrls Extra controls to pass to libldap.
181  * @return
182  * - 0 on success.
183  * - -1 on failure.
184  */
186  char const *bind_dn, char const *password,
187  LDAPControl **serverctrls, LDAPControl **clientctrls)
188 {
189  int fd = -1;
190  fr_ldap_bind_ctx_t *bind_ctx;
192 
193  DEBUG2("Starting bind operation");
194 
195  MEM(bind_ctx = talloc_zero(c, fr_ldap_bind_ctx_t));
196  bind_ctx->c = c;
197 
198  /*
199  * Bind as anonymous user
200  */
201  bind_ctx->bind_dn = bind_dn ? bind_dn : "";
202  bind_ctx->password = password;
203  bind_ctx->serverctrls = serverctrls;
204  bind_ctx->clientctrls = clientctrls;
205 
206  el = c->conn->el;
207 
208  /*
209  * ldap_get_option can return a LDAP_SUCCESS even if the fd is not yet available
210  * - hence the test for fd >= 0
211  */
212  if ((ldap_get_option(c->handle, LDAP_OPT_DESC, &fd) == LDAP_SUCCESS) && (fd >= 0)) {
213  int ret;
214 
215  ret = fr_event_fd_insert(bind_ctx, NULL, el, fd,
216  NULL,
219  bind_ctx);
220  if (!fr_cond_assert(ret == 0)) {
221  talloc_free(bind_ctx);
222  return -1;
223  }
224  } else {
225  /*
226  * Connections initialised with ldap_init() do not have a fd until
227  * the first request (usually bind) occurs - so this code path
228  * starts the bind process to open the connection.
229  */
230  _ldap_bind_io_write(el, -1, 0, bind_ctx);
231  }
232 
233  return 0;
234 }
235 
236 /** Yield interpreter after queueing LDAP bind
237  *
238  */
240  UNUSED request_t *request, UNUSED void *uctx)
241 {
242  return UNLANG_ACTION_YIELD;
243 }
244 
245 /** Handle the return code from parsed LDAP results to set the module rcode
246  *
247  */
248 static unlang_action_t ldap_async_auth_bind_results(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
249 {
250  fr_ldap_bind_auth_ctx_t *bind_auth_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_auth_ctx_t);
251  fr_ldap_bind_ctx_t *bind_ctx = bind_auth_ctx->bind_ctx;
252  rlm_rcode_t rcode = RLM_MODULE_OK;
253 
254  switch (bind_auth_ctx->ret) {
255  case LDAP_PROC_SUCCESS:
256  RDEBUG2("Bind as user \"%s\" was successful", bind_ctx->bind_dn);
257  break;
258 
260  RDEBUG2("Bind as user \"%s\" not permitted", bind_ctx->bind_dn);
261  rcode = RLM_MODULE_DISALLOW;
262  break;
263 
264  case LDAP_PROC_REJECT:
265  RDEBUG2("Bind as user \"%s\" rejected", bind_ctx->bind_dn);
266  rcode = RLM_MODULE_REJECT;
267  break;
268 
269  case LDAP_PROC_BAD_DN:
270  rcode = RLM_MODULE_INVALID;
271  break;
272 
273  case LDAP_PROC_NO_RESULT:
274  rcode = RLM_MODULE_NOTFOUND;
275  break;
276 
277  default:
278  rcode = RLM_MODULE_FAIL;
279  break;
280  }
281 
282  if (bind_auth_ctx->treq) {
283  /*
284  * Bind auth ctx is freed by trunk request free.
285  */
286  fr_trunk_request_signal_complete(bind_auth_ctx->treq);
287  } else {
288  /*
289  * If there is no trunk request, the request failed, and we need to free the ctx
290  */
291  talloc_free(bind_auth_ctx);
292  }
293 
294  RETURN_MODULE_RCODE(rcode);
295 }
296 
297 /** Signal an outstanding LDAP bind request to cancel
298  *
299  */
300 static void ldap_async_auth_bind_cancel(request_t *request, UNUSED fr_signal_t action, void *uctx)
301 {
302  fr_ldap_bind_auth_ctx_t *bind_auth_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_auth_ctx_t);
303 
304  RWARN("Cancelling bind auth");
305  if (bind_auth_ctx->msgid > 0) fr_rb_remove(bind_auth_ctx->thread->binds, bind_auth_ctx);
306  fr_trunk_request_signal_cancel(bind_auth_ctx->treq);
307 }
308 
309 /** Initiate an async LDAP bind for authentication
310  *
311  * @param[in] request this bind relates to.
312  * @param[in] thread whose connection the bind should be performed on.
313  * @param[in] bind_dn Identity to bind with.
314  * @param[in] password Password to bind with.
315  * @return
316  * - 0 on success.
317  * - -1 on failure.
318  */
319 unlang_action_t fr_ldap_bind_auth_async(request_t *request, fr_ldap_thread_t *thread, char const *bind_dn, char const *password)
320 {
321  fr_ldap_bind_auth_ctx_t *bind_auth_ctx;
322  fr_trunk_request_t *treq;
324  fr_trunk_enqueue_t ret;
325 
326  if (!ttrunk) {
327  ERROR("Failed to get trunk connection for LDAP bind");
328  return UNLANG_ACTION_FAIL;
329  }
330 
331  treq = fr_trunk_request_alloc(ttrunk->trunk, request);
332  if (!treq) {
333  ERROR ("Failed to allocate trunk request for LDAP bind");
334  return UNLANG_ACTION_FAIL;
335  }
336 
337  MEM(bind_auth_ctx = talloc(treq, fr_ldap_bind_auth_ctx_t));
338  *bind_auth_ctx = (fr_ldap_bind_auth_ctx_t) {
339  .treq = treq,
340  .request = request,
341  .thread = thread,
342  .ret = LDAP_PROC_NO_RESULT
343  };
344 
345  MEM(bind_auth_ctx->bind_ctx = talloc(bind_auth_ctx, fr_ldap_bind_ctx_t));
346  *bind_auth_ctx->bind_ctx = (fr_ldap_bind_ctx_t) {
347  .bind_dn = bind_dn,
348  .password = password
349  };
350 
351  ret = fr_trunk_request_enqueue(&bind_auth_ctx->treq, ttrunk->trunk, request, bind_auth_ctx, NULL);
352 
353  switch (ret) {
354  case FR_TRUNK_ENQUEUE_OK:
356  break;
357 
358  default:
359  ERROR("Failed to enqueue bind request");
360  fr_trunk_request_free(&treq);
361  return UNLANG_ACTION_FAIL;
362  }
363 
364  return unlang_function_push(request,
368  ~FR_SIGNAL_CANCEL, UNLANG_SUB_FRAME,
369  bind_auth_ctx);
370 }
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_YIELD
Temporarily pause execution until an event occurs.
Definition: action.h:42
static void ldap_async_auth_bind_cancel(request_t *request, UNUSED fr_signal_t action, void *uctx)
Signal an outstanding LDAP bind request to cancel.
Definition: bind.c:300
int fr_ldap_bind_async(fr_ldap_connection_t *c, char const *bind_dn, char const *password, LDAPControl **serverctrls, LDAPControl **clientctrls)
Install I/O handlers for the bind operation.
Definition: bind.c:185
static unlang_action_t ldap_async_auth_bind_results(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Handle the return code from parsed LDAP results to set the module rcode.
Definition: bind.c:248
static void _ldap_bind_io_read(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
Parse a bind response from a server.
Definition: bind.c:56
static USES_APPLE_DEPRECATED_API void _ldap_bind_io_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, UNUSED int fd_errno, void *uctx)
Error reading from or writing to the file descriptor.
Definition: bind.c:39
static unlang_action_t ldap_async_auth_bind_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, UNUSED void *uctx)
Yield interpreter after queueing LDAP bind.
Definition: bind.c:239
unlang_action_t fr_ldap_bind_auth_async(request_t *request, fr_ldap_thread_t *thread, char const *bind_dn, char const *password)
Initiate an async LDAP bind for authentication.
Definition: bind.c:319
static void _ldap_bind_io_write(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Send a bind request to a server.
Definition: bind.c:96
#define USES_APPLE_DEPRECATED_API
Definition: build.h:431
#define RCSID(id)
Definition: build.h:444
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:137
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define fr_event_fd_insert(...)
Definition: event.h:232
#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
LDAPControl ** clientctrls
Controls to pass to the client (library).
Definition: base.h:493
fr_ldap_rcode_t ret
Return code of bind operation.
Definition: base.h:619
int msgid
libldap msgid for this bind.
Definition: base.h:612
char * server
Initial server to bind to.
Definition: base.h:222
LDAP * handle
libldap handle.
Definition: base.h:331
void fr_ldap_control_merge(LDAPControl *serverctrls_out[], LDAPControl *clientctrls_out[], size_t serverctrls_len, size_t clientctrls_len, fr_ldap_connection_t *conn, LDAPControl *serverctrls_in[], LDAPControl *clientctrls_in[])
Merge connection and call specific client and server controls.
Definition: control.c:48
fr_connection_t * conn
Connection state handle.
Definition: base.h:343
int fd
File descriptor for this connection.
Definition: base.h:347
void fr_ldap_state_error(fr_ldap_connection_t *c)
Signal that there's been an error on the connection.
Definition: state.c:134
int msgid
Of the bind operation. Only used when binding as admin.
Definition: base.h:495
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition: base.h:342
fr_ldap_connection_t * c
to bind. Only used when binding as admin user.
Definition: base.h:489
fr_ldap_state_t fr_ldap_state_next(fr_ldap_connection_t *c)
Move between LDAP connection states.
Definition: state.c:49
LDAPControl ** serverctrls
Controls to pass to the server.
Definition: base.h:492
fr_ldap_thread_trunk_t * fr_thread_ldap_bind_trunk_get(fr_ldap_thread_t *thread)
Find the thread specific trunk to use for LDAP bind auths.
Definition: connection.c:1367
#define LDAP_MAX_CONTROLS
Maximum number of client/server controls.
Definition: base.h:94
fr_trunk_t * trunk
Connection trunk.
Definition: base.h:403
int fr_ldap_connection_timeout_reset(fr_ldap_connection_t const *conn)
Definition: connection.c:425
char const * bind_dn
of the user, may be NULL to bind anonymously.
Definition: base.h:490
fr_ldap_thread_t * thread
This bind is being run by.
Definition: base.h:610
fr_rb_tree_t * binds
Tree of outstanding bind auths.
Definition: base.h:386
fr_trunk_request_t * treq
Trunk request this bind is associated with.
Definition: base.h:611
char const * password
of the user, may be NULL if no password is specified.
Definition: base.h:491
fr_ldap_rcode_t
Codes returned by fr_ldap internal functions.
Definition: base.h:577
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition: base.h:580
@ LDAP_PROC_NOT_PERMITTED
Operation was not permitted, either current user was locked out in the case of binds,...
Definition: base.h:587
@ LDAP_PROC_REJECT
Bind failed, user was rejected.
Definition: base.h:591
@ LDAP_PROC_BAD_DN
Specified an invalid object in a bind or search DN.
Definition: base.h:593
@ LDAP_PROC_NO_RESULT
Got no results.
Definition: base.h:595
Holds arguments for async bind auth requests.
Definition: base.h:608
Holds arguments for the async bind operation.
Definition: base.h:488
Tracks the state of a libldap connection handle.
Definition: base.h:330
Thread specific structure to manage LDAP trunk connections.
Definition: base.h:379
Thread LDAP trunk structure.
Definition: base.h:397
fr_ldap_rcode_t fr_ldap_result(LDAPMessage **result, LDAPControl ***ctrls, fr_ldap_connection_t const *conn, int msgid, int all, char const *dn, fr_time_delta_t timeout)
Parse response from LDAP server dealing with any errors.
Definition: base.c:449
#define PERROR(_fmt,...)
Definition: log.h:228
#define RWARN(fmt,...)
Definition: log.h:297
talloc_free(reap)
Stores all information relating to an event list.
Definition: event.c:411
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define DEBUG2(fmt,...)
Definition: radclient.h:43
void * fr_rb_remove(fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
Definition: rb.c:691
#define RETURN_MODULE_RCODE(_rcode)
Definition: rcode.h:64
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition: rcode.h:45
@ RLM_MODULE_OK
The module is OK, continue.
Definition: rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition: rcode.h:42
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition: rcode.h:46
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition: rcode.h:41
@ RLM_MODULE_NOTFOUND
User not found.
Definition: rcode.h:47
fr_signal_t
Definition: signal.h:48
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
#define fr_time_delta_wrap(_time)
Definition: time.h:152
void fr_trunk_request_free(fr_trunk_request_t **treq_to_free)
If the trunk request is freed then update the target requests.
Definition: trunk.c:2217
void fr_trunk_request_signal_cancel(fr_trunk_request_t *treq)
Cancel a trunk request.
Definition: trunk.c:2047
fr_trunk_request_t * fr_trunk_request_alloc(fr_trunk_t *trunk, request_t *request)
(Pre-)Allocate a new trunk request
Definition: trunk.c:2369
fr_trunk_enqueue_t fr_trunk_request_enqueue(fr_trunk_request_t **treq_out, fr_trunk_t *trunk, request_t *request, void *preq, void *rctx)
Enqueue a request that needs data written to the trunk.
Definition: trunk.c:2481
void fr_trunk_request_signal_complete(fr_trunk_request_t *treq)
Signal that a trunk request is complete.
Definition: trunk.c:1995
Wraps a normal request.
Definition: trunk.c:97
fr_trunk_enqueue_t
Definition: trunk.h:148
@ FR_TRUNK_ENQUEUE_IN_BACKLOG
Request should be enqueued in backlog.
Definition: trunk.h:149
@ FR_TRUNK_ENQUEUE_OK
Operation was successful.
Definition: trunk.h:150
static fr_event_list_t * el