The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
bind.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: 5b4bb23cb63a2a4c3f60c1457e06b086c44e8db9 $
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 */
24RCSID("$Id: 5b4bb23cb63a2a4c3f60c1457e06b086c44e8db9 $")
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 */
56static 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) {
69 return;
70
72 DEBUG2("Bind as \"%s\" to \"%s\" successful",
73 *bind_ctx->bind_dn ? bind_ctx->bind_dn : "(anonymous)", c->config->server);
74 fr_ldap_state_next(c); /* onto the next operation */
75 break;
76
78 PERROR("Bind as \"%s\" to \"%s\" not permitted",
79 *bind_ctx->bind_dn ? bind_ctx->bind_dn : "(anonymous)", c->config->server);
80 talloc_free(bind_ctx); /* Also removes fd events */
81 fr_ldap_state_error(c); /* Restart the connection state machine */
82 return;
83
84 default:
85 PERROR("Bind as \"%s\" to \"%s\" failed",
86 *bind_ctx->bind_dn ? bind_ctx->bind_dn : "(anonymous)", c->config->server);
87 talloc_free(bind_ctx); /* Also removes fd events */
88 fr_ldap_state_error(c); /* Restart the connection state machine */
89 return;
90 }
91 talloc_free(bind_ctx); /* Also removes fd events */
92}
93
94/** Send a bind request to a server
95 *
96 * @param[in] el the event occurred in.
97 * @param[in] fd the event occurred on.
98 * @param[in] flags from kevent.
99 * @param[in] uctx bind_ctx containing credentials, and connection config/handle.
100 */
101static void _ldap_bind_io_write(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
102{
103 fr_ldap_bind_ctx_t *bind_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_ctx_t);
104 fr_ldap_connection_t *c = bind_ctx->c;
105
106 LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS];
107 LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS];
108
109 int ret;
110 struct berval cred;
111
112 fr_ldap_control_merge(our_serverctrls, our_clientctrls,
113 NUM_ELEMENTS(our_serverctrls),
114 NUM_ELEMENTS(our_clientctrls),
115 c, bind_ctx->serverctrls, bind_ctx->clientctrls);
116
117 if (bind_ctx->password) {
118 memcpy(&cred.bv_val, &bind_ctx->password, sizeof(cred.bv_val));
119 cred.bv_len = talloc_strlen(bind_ctx->password);
120 } else {
121 cred.bv_val = NULL;
122 cred.bv_len = 0;
123 }
124
125 /*
126 * Yes, confusingly named. This is the simple version
127 * of the SASL bind function that should always be
128 * available.
129 */
130 ret = ldap_sasl_bind(c->handle, bind_ctx->bind_dn, LDAP_SASL_SIMPLE, &cred,
131 our_serverctrls, our_clientctrls, &bind_ctx->msgid);
132 switch (ret) {
133 /*
134 * If the handle was not connected, this operation
135 * can return either LDAP_X_CONNECTING or LDAP_SUCCESS
136 * depending on how fast the connection came up
137 * and whether it was connectionless.
138 */
139 case LDAP_X_CONNECTING: /* Connection in progress - retry later */
140 ret = ldap_get_option(c->handle, LDAP_OPT_DESC, &fd);
141 if (!fr_cond_assert(ret == LDAP_OPT_SUCCESS)) {
142 error:
143 talloc_free(bind_ctx);
144 fr_ldap_state_error(c); /* Restart the connection state machine */
145 return;
146 }
147
148 ret = fr_event_fd_insert(bind_ctx, NULL, el, fd,
149 NULL,
150 _ldap_bind_io_write, /* We'll be called again when the conn is open */
152 bind_ctx);
153 if (!fr_cond_assert(ret == 0)) goto error;
154 break;
155
156 case LDAP_SUCCESS:
157 if (fd < 0 ) {
158 ret = ldap_get_option(c->handle, LDAP_OPT_DESC, &fd);
159 if ((ret != LDAP_OPT_SUCCESS) || (fd < 0)) goto error;
160 }
161 c->fd = fd;
162
163 /*
164 * libldap, up to at least 2.7.2, switches the socket to blocking mode
165 * while sending the first request after an async connect.
166 */
167 if (fr_nonblock(fd) < 0) {
168 PERROR("Failed setting LDAP connection non-blocking");
169 goto error;
170 }
171
172 ret = fr_event_fd_insert(bind_ctx, NULL, el, fd,
174 NULL,
176 bind_ctx);
177 if (!fr_cond_assert(ret == 0)) goto error;
178 break;
179
180 default:
181 ERROR("Bind failed: %s", ldap_err2string(ret));
182 goto error;
183 }
184}
185
186/** Install I/O handlers for the bind operation
187 *
188 * @param[in] c connection to StartTLS on.
189 * @param[in] bind_dn Identity to bind with.
190 * @param[in] password Password to bind with.
191 * @param[in] serverctrls Extra controls to pass to the server.
192 * @param[in] clientctrls Extra controls to pass to libldap.
193 * @return
194 * - 0 on success.
195 * - -1 on failure.
196 */
198 char const *bind_dn, char const *password,
199 LDAPControl **serverctrls, LDAPControl **clientctrls)
200{
201 int fd = -1;
202 fr_ldap_bind_ctx_t *bind_ctx;
204
205 DEBUG2("Starting bind operation");
206
207 MEM(bind_ctx = talloc_zero(c, fr_ldap_bind_ctx_t));
208 bind_ctx->c = c;
209
210 /*
211 * Bind as anonymous user
212 */
213 bind_ctx->bind_dn = bind_dn ? bind_dn : "";
214 bind_ctx->password = password;
215 bind_ctx->serverctrls = serverctrls;
216 bind_ctx->clientctrls = clientctrls;
217
218 el = c->conn->el;
219
220 /*
221 * ldap_get_option can return a LDAP_SUCCESS even if the fd is not yet available
222 * - hence the test for fd >= 0
223 */
224 if ((ldap_get_option(c->handle, LDAP_OPT_DESC, &fd) == LDAP_SUCCESS) && (fd >= 0)) {
225 int ret;
226
227 ret = fr_event_fd_insert(bind_ctx, NULL, el, fd,
228 NULL,
231 bind_ctx);
232 if (!fr_cond_assert(ret == 0)) {
233 talloc_free(bind_ctx);
234 return -1;
235 }
236 } else {
237 /*
238 * Connections initialised with ldap_init() do not have a fd until
239 * the first request (usually bind) occurs - so this code path
240 * starts the bind process to open the connection.
241 */
242 _ldap_bind_io_write(el, -1, 0, bind_ctx);
243 }
244
245 return 0;
246}
247
248/** Yield interpreter after queueing LDAP bind
249 *
250 */
255
256/** Handle the return code from parsed LDAP results to set the module rcode
257 *
258 */
260{
261 fr_ldap_bind_auth_ctx_t *bind_auth_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_auth_ctx_t);
262 fr_ldap_bind_ctx_t *bind_ctx = bind_auth_ctx->bind_ctx;
264
265 switch (bind_auth_ctx->ret) {
267 RDEBUG2("Bind as user \"%s\" was successful", bind_ctx->bind_dn);
268 break;
269
271 RDEBUG2("Bind as user \"%s\" not permitted", bind_ctx->bind_dn);
272 rcode = RLM_MODULE_DISALLOW;
273 break;
274
275 case LDAP_PROC_REJECT:
276 RDEBUG2("Bind as user \"%s\" rejected", bind_ctx->bind_dn);
277 rcode = RLM_MODULE_REJECT;
278 break;
279
280 case LDAP_PROC_BAD_DN:
281 rcode = RLM_MODULE_INVALID;
282 break;
283
285 rcode = RLM_MODULE_NOTFOUND;
286 break;
287
288 default:
289 rcode = RLM_MODULE_FAIL;
290 break;
291 }
292
293 if (bind_auth_ctx->treq) {
294 /*
295 * Bind auth ctx is freed by trunk request free.
296 */
297 trunk_request_signal_complete(bind_auth_ctx->treq);
298 } else {
299 /*
300 * If there is no trunk request, the request failed, and we need to free the ctx
301 */
302 talloc_free(bind_auth_ctx);
303 }
304
305 RETURN_UNLANG_RCODE(rcode);
306}
307
308/** Signal an outstanding LDAP bind request to cancel
309 *
310 */
312{
313 fr_ldap_bind_auth_ctx_t *bind_auth_ctx = talloc_get_type_abort(uctx, fr_ldap_bind_auth_ctx_t);
314
315 RWARN("Cancelling bind auth");
316 if (bind_auth_ctx->msgid > 0) fr_rb_remove(NULL, bind_auth_ctx->thread->binds, bind_auth_ctx);
317 trunk_request_signal_cancel(bind_auth_ctx->treq);
318}
319
320/** Initiate an async LDAP bind for authentication
321 *
322 * @param[out] p_result Where to write the result of the bind operation.
323 * If this is NULL, the rcode result will be discarded.
324 * - LDAP_PROC_SUCCES = RLM_MODULE_OK,
325 * - LDAP_PROC_NOT_PERMITTED = RLM_MODULE_DISALLOW,
326 * - LDAP_PROC_REJECT = RLM_MODULE_REJECT,
327 * - LDAP_PROC_BAD_DN = RLM_MODULE_INVALID,
328 * - LDAP_PROC_NO_RESULT = RLM_MODULE_NOTFOUND
329 * - * = RLM_MODULE_FAIL.
330 * @param[in] request this bind relates to.
331 * @param[in] thread whose connection the bind should be performed on.
332 * @param[in] bind_dn Identity to bind with.
333 * @param[in] password Password to bind with.
334 * @return
335 * - 0 on success.
336 * - -1 on failure.
337 */
338unlang_action_t fr_ldap_bind_auth_async(unlang_result_t *p_result, request_t *request, fr_ldap_thread_t *thread, char const *bind_dn, char const *password)
339{
340 fr_ldap_bind_auth_ctx_t *bind_auth_ctx;
341 trunk_request_t *treq;
343 trunk_enqueue_t ret;
344
345 if (!password || (password[0] == '\0')) {
346 REDEBUG("Refusing to bind as \"%s\" with an empty password - this would be an unauthenticated bind",
347 bind_dn ? bind_dn : "");
349 }
350
351 if (!ttrunk) {
352 ERROR("Failed to get trunk connection for LDAP bind");
354 }
355
356 treq = trunk_request_alloc(ttrunk->trunk, request);
357 if (!treq) {
358 ERROR ("Failed to allocate trunk request for LDAP bind");
360 }
361
362 MEM(bind_auth_ctx = talloc(treq, fr_ldap_bind_auth_ctx_t));
363 *bind_auth_ctx = (fr_ldap_bind_auth_ctx_t) {
364 .treq = treq,
365 .request = request,
366 .thread = thread,
368 };
369
370 MEM(bind_auth_ctx->bind_ctx = talloc(bind_auth_ctx, fr_ldap_bind_ctx_t));
371 *bind_auth_ctx->bind_ctx = (fr_ldap_bind_ctx_t) {
372 .bind_dn = bind_dn,
373 .password = password
374 };
375
376 ret = trunk_request_enqueue(&bind_auth_ctx->treq, ttrunk->trunk, request, bind_auth_ctx, NULL);
377
378 switch (ret) {
379 case TRUNK_ENQUEUE_OK:
381 break;
382
383 default:
384 ERROR("Failed to enqueue bind request");
385 trunk_request_free(&treq);
387 }
388
389 return unlang_function_push_with_result(p_result,
390 request,
395 bind_auth_ctx);
396}
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ 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:311
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:197
static unlang_action_t ldap_async_auth_bind_start(UNUSED unlang_result_t *p_result, UNUSED request_t *request, UNUSED void *uctx)
Yield interpreter after queueing LDAP bind.
Definition bind.c:251
static unlang_action_t ldap_async_auth_bind_results(unlang_result_t *p_result, request_t *request, void *uctx)
Handle the return code from parsed LDAP results to set the module rcode.
Definition bind.c:259
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
unlang_action_t fr_ldap_bind_auth_async(unlang_result_t *p_result, 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:338
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:101
static uint8_t request[4]
#define USES_APPLE_DEPRECATED_API
Definition build.h:590
#define RCSID(id)
Definition build.h:603
#define UNUSED
Definition build.h:415
#define NUM_ELEMENTS(_t)
Definition build.h:437
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:177
#define MEM(x)
Definition debug.h:38
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define fr_event_fd_insert(...)
Definition event.h:247
#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:146
talloc_free(hp)
#define UNLANG_SUB_FRAME
Definition interpret.h:37
LDAPControl ** clientctrls
Controls to pass to the client (library).
Definition base.h:483
fr_ldap_rcode_t ret
Return code of bind operation.
Definition base.h:611
int msgid
libldap msgid for this bind.
Definition base.h:604
char * server
Initial server to bind to.
Definition base.h:233
LDAP * handle
libldap handle.
Definition base.h:327
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
int fd
File descriptor for this connection.
Definition base.h:343
void fr_ldap_state_error(fr_ldap_connection_t *c)
Signal that there's been an error on the connection.
Definition state.c:153
int msgid
Of the bind operation. Only used when binding as admin.
Definition base.h:485
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition base.h:338
fr_ldap_connection_t * c
to bind. Only used when binding as admin user.
Definition base.h:479
trunk_request_t * treq
Trunk request this bind is associated with.
Definition base.h:603
fr_ldap_state_t fr_ldap_state_next(fr_ldap_connection_t *c)
Move between LDAP connection states.
Definition state.c:50
LDAPControl ** serverctrls
Controls to pass to the server.
Definition base.h:482
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.
#define LDAP_MAX_CONTROLS
Maximum number of client/server controls.
Definition base.h:94
fr_ldap_rcode_t
Codes returned by fr_ldap internal functions.
Definition base.h:567
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition base.h:572
@ LDAP_PROC_NOT_PERMITTED
Operation was not permitted, either current user was locked out in the case of binds,...
Definition base.h:579
@ LDAP_PROC_PENDING
fr_ldap_result() was polling and hasn't read the final message yet.
Definition base.h:568
@ LDAP_PROC_REJECT
Bind failed, user was rejected.
Definition base.h:583
@ LDAP_PROC_BAD_DN
Specified an invalid object in a bind or search DN.
Definition base.h:585
@ LDAP_PROC_NO_RESULT
Got no results.
Definition base.h:587
char const * bind_dn
of the user, may be NULL to bind anonymously.
Definition base.h:480
trunk_t * trunk
Connection trunk.
Definition base.h:392
fr_ldap_thread_t * thread
This bind is being run by.
Definition base.h:602
connection_t * conn
Connection state handle.
Definition base.h:339
fr_rb_tree_t * binds
Tree of outstanding bind auths.
Definition base.h:373
char const * password
of the user, may be NULL if no password is specified.
Definition base.h:481
Holds arguments for async bind auth requests.
Definition base.h:600
Holds arguments for the async bind operation.
Definition base.h:478
Tracks the state of a libldap connection handle.
Definition base.h:326
Thread specific structure to manage LDAP trunk connections.
Definition base.h:366
Thread LDAP trunk structure.
Definition base.h:386
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:409
#define PERROR(_fmt,...)
Definition log.h:233
#define RWARN(fmt,...)
Definition log.h:314
Stores all information relating to an event list.
Definition event.c:375
int fr_nonblock(UNUSED int fd)
Definition misc.c:293
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define DEBUG2(fmt,...)
int fr_rb_remove(void **removed, fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
Definition rb.c:718
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:51
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:52
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:47
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:53
#define RETURN_UNLANG_INVALID
Definition rcode.h:72
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:67
#define RETURN_UNLANG_FAIL
Definition rcode.h:69
#define FR_SIGNAL_ALLOW(_signals)
Signal mask which blocks every signal except the ones in _signals.
Definition signal.h:58
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
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
#define fr_time_delta_wrap(_time)
Definition time.h:152
trunk_request_t * trunk_request_alloc(trunk_t *trunk, request_t *request)
(Pre-)Allocate a new trunk request
Definition trunk.c:2542
trunk_enqueue_t trunk_request_enqueue(trunk_request_t **treq_out, trunk_t *trunk, request_t *request, void *preq, void *rctx)
Enqueue a request that needs data written to the trunk.
Definition trunk.c:2657
void trunk_request_signal_cancel(trunk_request_t *treq)
Cancel a trunk request.
Definition trunk.c:2216
void trunk_request_free(trunk_request_t **treq_to_free)
If the trunk request is freed then update the target requests.
Definition trunk.c:2386
void trunk_request_signal_complete(trunk_request_t *treq)
Signal that a trunk request is complete.
Definition trunk.c:2158
Wraps a normal request.
Definition trunk.c:99
trunk_enqueue_t
Definition trunk.h:158
@ TRUNK_ENQUEUE_OK
Operation was successful.
Definition trunk.h:160
@ TRUNK_ENQUEUE_IN_BACKLOG
Request should be enqueued in backlog.
Definition trunk.h:159
static fr_event_list_t * el