The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
start_tls.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: 7f03983b541acd48e9ebe89ecc7e0c5e95c4ae4c $
19 * @file lib/ldap/start_tls.c
20 * @brief Start TLS asynchronously
21 *
22 * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
24RCSID("$Id: 7f03983b541acd48e9ebe89ecc7e0c5e95c4ae4c $")
25
27
28#include <freeradius-devel/ldap/base.h>
29#include <freeradius-devel/util/debug.h>
30
31/** Holds arguments for the start_tls operation
32 *
33 */
34typedef struct {
35 fr_ldap_connection_t *c; //!< The current connection.
36 LDAPControl **serverctrls; //!< Controls to pass to the server.
37 LDAPControl **clientctrls; //!< Controls to pass to the client (library).
38
39 int msgid;
41
42/** Error reading from or writing to the file descriptor
43 *
44 * @param[in] el the event occurred in.
45 * @param[in] fd the event occurred on.
46 * @param[in] flags from kevent.
47 * @param[in] fd_errno The error that occurred.
48 * @param[in] uctx Connection config and handle.
49 */
51 UNUSED int fd_errno, void *uctx)
52{
53 fr_ldap_start_tls_ctx_t *tls_ctx = talloc_get_type_abort(uctx, fr_ldap_start_tls_ctx_t);
54 fr_ldap_connection_t *c = tls_ctx->c;
55
56 talloc_free(tls_ctx);
57 fr_ldap_state_error(c); /* Restart the connection state machine */
58}
59
60/*
61 * TLS handshake call flow. Entries marked [< 2.6.9] or [>= 2.6.9] only
62 * apply to those versions of libldap. 2.6.9 is where ITS#8047 made the
63 * handshake resumable.
64 *
65 * - ldap_install_tls
66 * - calls ldap_tls_inplace to check if the Sockbuf for defconn has TLS installed
67 * - If it does, returns LDAP_LOCAL_ERROR (and we fail).
68 * - [>= 2.6.9] Not if defconn's status is LDAP_CONNST_TLS_INPROGRESS, which is
69 * what lets a second call resume a handshake.
70 * - calls ldap_int_tls_start.
71 * - calls tls_init (to initialise ssl library - only done once per implementation).
72 * - if net timeout is >= 0, and LDAP_OPT_CONNECT_ASYNC is off, then set the FD to
73 * nonblocking mode. With LDAP_OPT_CONNECT_ASYNC on it's already nonblocking.
74 * - calls ldap_int_tls_connect
75 * - either gets existing session or
76 * - installs sockbuff shims to do tls encode/decode.
77 * - calls connect callback
78 * - [>= 2.6.9] sets defconn's status to LDAP_CONNST_TLS_INPROGRESS.
79 * - calls ->ti_session_connect (ssl library callback)
80 * - calls tlso_session_connect (openssl shim)
81 * - calls SSL_connect - SSL_connect can be called multiple times
82 * to continue session negotiation.
83 * returns 0 on success, -1 on error.
84 * - on -1, calls update_flags, which calls tlso_session_upflags
85 * - calls SSL_get_error, which returns SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,
86 * SSL_ERROR_WANT_CONNECT, or another error. If error code is one of the above
87 * returns 1, else returns 0.
88 * Sets sb->sb_trans_needs_read or sb->sb_trans_needs_write.
89 * - if update_flags returns 1 ldap_int_tls_connect returns 1.
90 * - [>= 2.6.9] sets defconn's status to LDAP_CONNST_CONNECTED on success or error.
91 * - while ldap_int_tls_connect returns 1:
92 * - [< 2.6.9] with LDAP_OPT_CONNECT_ASYNC on, calls ldap_int_poll, waiting for
93 * what remains of the net timeout. The deadline and fd are only set up if the
94 * net timeout is >= 0, and a net timeout of 0 fails on the first wait.
95 * - [< 2.6.9] with LDAP_OPT_CONNECT_ASYNC off, calls ldap_int_tls_connect again
96 * without waiting.
97 * - [>= 2.6.9] with LDAP_OPT_CONNECT_ASYNC on, returns LDAP_X_CONNECTING. Calling
98 * ldap_install_tls again continues the handshake.
99 * - [>= 2.6.9] with LDAP_OPT_CONNECT_ASYNC off, calls ldap_int_poll, waiting for
100 * what remains of the net timeout.
101 * - [>= 2.6.9] with LDAP_OPT_CONNECT_ASYNC off, and a net timeout >= 0, sets the FD
102 * back to blocking mode.
103 * - returns LDAP_TIMEOUT if we hit the timeout.
104 *
105 * Our handles always have LDAP_OPT_CONNECT_ASYNC on, and a net timeout of 0. So
106 * from 2.6.9 we drive the handshake from the event loop. Before 2.6.9 the only
107 * way to complete the handshake is to put the socket into blocking mode, so
108 * SSL_connect never asks to wait.
109 */
110#if LDAP_VENDOR_VERSION >= 20609
111static void _ldap_start_tls_io_handshake(fr_event_list_t *el, int fd, int flags, void *uctx);
112
113/** Run the TLS handshake as far as it can go without waiting
114 *
115 * If the handshake needs to wait, install an I/O handler for whichever
116 * of read or write the TLS library is waiting on, and continue when
117 * that handler is called.
118 *
119 * @param[in] el to install I/O handlers in.
120 * @param[in] tls_ctx of the StartTLS operation. Freed when the handshake
121 * completes or fails.
122 */
124{
125 fr_ldap_connection_t *c = tls_ctx->c;
126 Sockbuf *sb = NULL;
127 int fd = -1;
128 int ret;
129 bool need_read, need_write;
130
131 ret = ldap_install_tls(c->handle);
132 switch (ret) {
133 case LDAP_SUCCESS:
134 talloc_free(tls_ctx);
135 fr_ldap_state_next(c); /* onto the next operation */
136 return;
137
138 case LDAP_X_CONNECTING:
139 if ((ldap_get_option(c->handle, LDAP_OPT_SOCKBUF, &sb) != LDAP_OPT_SUCCESS) ||
140 (ldap_get_option(c->handle, LDAP_OPT_DESC, &fd) != LDAP_OPT_SUCCESS) || (fd < 0)) {
141 ERROR("Failed getting the socket to continue the TLS handshake");
142 goto error;
143 }
144
145 need_read = ber_sockbuf_ctrl(sb, LBER_SB_OPT_NEEDS_READ, NULL);
146 need_write = ber_sockbuf_ctrl(sb, LBER_SB_OPT_NEEDS_WRITE, NULL);
147 if (!fr_cond_assert_msg(need_read || need_write,
148 "TLS handshake waiting on neither read nor write")) goto error;
149
150 if (fr_event_fd_insert(tls_ctx, NULL, el, fd,
151 need_read ? _ldap_start_tls_io_handshake : NULL,
152 need_write ? _ldap_start_tls_io_handshake : NULL,
154 tls_ctx) < 0) {
155 PERROR("Failed inserting TLS handshake I/O handler");
156 goto error;
157 }
158 return;
159
160 default:
161 ERROR("ldap_install_tls failed: %s", ldap_err2string(ret));
162 error:
163 talloc_free(tls_ctx);
164 fr_ldap_state_error(c); /* Restart the connection state machine */
165 return;
166 }
167}
168
169/** Continue the TLS handshake once the socket is ready
170 *
171 * @param[in] el the event occurred in.
172 * @param[in] fd the event occurred on.
173 * @param[in] flags from kevent.
174 * @param[in] uctx the StartTLS operation.
175 */
176static void _ldap_start_tls_io_handshake(fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
177{
178 ldap_start_tls_handshake(el, talloc_get_type_abort(uctx, fr_ldap_start_tls_ctx_t));
179}
180#else
181/** Run the TLS handshake to completion
182 *
183 * libldap before 2.6.9 can't resume a TLS handshake, so the socket is put
184 * back into blocking mode, and this call blocks the thread until the
185 * handshake completes or fails. Build against libldap 2.6.9 or later to
186 * avoid this.
187 *
188 * @param[in] el unused.
189 * @param[in] tls_ctx of the StartTLS operation. Always freed.
190 */
192{
193 fr_ldap_connection_t *c = tls_ctx->c;
194 Sockbuf *sb = NULL;
195 int ret;
196
197 talloc_free(tls_ctx);
198
199 if (ldap_get_option(c->handle, LDAP_OPT_SOCKBUF, &sb) != LDAP_OPT_SUCCESS) {
200 ERROR("Failed getting the socket for the TLS handshake");
201 error:
202 fr_ldap_state_error(c); /* Restart the connection state machine */
203 return;
204 }
205
206 ber_sockbuf_ctrl(sb, LBER_SB_OPT_SET_NONBLOCK, (void *)0);
207 ret = ldap_install_tls(c->handle);
208 ber_sockbuf_ctrl(sb, LBER_SB_OPT_SET_NONBLOCK, (void *)1);
209
210 if (ret != LDAP_SUCCESS) {
211 ERROR("ldap_install_tls failed: %s", ldap_err2string(ret));
212 goto error;
213 }
214
215 fr_ldap_state_next(c); /* onto the next operation */
216}
217#endif
218
219/** Event handler for the response to the StartTLS extended operation
220 *
221 * Once the server has agreed to StartTLS, run the TLS handshake.
222 *
223 * @param[in] el the event occurred in.
224 * @param[in] fd the event occurred on.
225 * @param[in] flags from kevent.
226 * @param[in] uctx Connection config and handle.
227 */
228static void _ldap_start_tls_io_read(fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
229{
230 fr_ldap_start_tls_ctx_t *tls_ctx = talloc_get_type_abort(uctx, fr_ldap_start_tls_ctx_t);
231 fr_ldap_connection_t *c = tls_ctx->c;
232 fr_ldap_rcode_t status;
233
234 /*
235 * We're I/O driven, if there's no data someone lied to us
236 */
237 status = fr_ldap_result(NULL, NULL, c, tls_ctx->msgid, LDAP_MSG_ALL, NULL, fr_time_delta_wrap(0));
238 switch (status) {
240 return;
241
244 break;
245
246 default:
247 talloc_free(tls_ctx);
248 PERROR("StartTLS failed");
249 fr_ldap_state_error(c); /* Restart the connection state machine */
250 break;
251 }
252}
253
254/** Send an extended operation to the LDAP server, requesting a transition to TLS
255 *
256 * Behind the scenes ldap_start_tls calls:
257 *
258 * ldap_extended_operation(ld, LDAP_EXOP_START_TLS, NULL, serverctrls, clientctrls, msgidp);
259 *
260 * @param[in] el the event occurred in.
261 * @param[in] fd the event occurred on.
262 * @param[in] flags from kevent.
263 * @param[in] uctx Connection config and handle.
264 */
265static void _ldap_start_tls_io_write(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
266{
267 fr_ldap_start_tls_ctx_t *tls_ctx = talloc_get_type_abort(uctx, fr_ldap_start_tls_ctx_t);
268 fr_ldap_connection_t *c = tls_ctx->c;
269
270 int ret;
271
272 LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS];
273 LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS];
274
275 fr_ldap_control_merge(our_serverctrls, our_clientctrls,
276 NUM_ELEMENTS(our_serverctrls),
277 NUM_ELEMENTS(our_clientctrls),
278 c, tls_ctx->serverctrls, tls_ctx->clientctrls);
279
280 ret = ldap_start_tls(c->handle, our_serverctrls, our_clientctrls, &tls_ctx->msgid);
281 /*
282 * If the handle was not connected, this operation
283 * can return either LDAP_X_CONNECTING or LDAP_SUCCESS
284 * depending on how fast the connection came up
285 * and whether it was connectionless.
286 */
287 switch (ret) {
288 case LDAP_X_CONNECTING: /* Connection in progress - retry later */
289 ret = ldap_get_option(c->handle, LDAP_OPT_DESC, &fd);
290 if (!fr_cond_assert(ret == LDAP_OPT_SUCCESS)) {
291 error:
292 talloc_free(tls_ctx);
293 fr_ldap_state_error(c); /* Restart the connection state machine */
294 return;
295 }
296
297 ret = fr_event_fd_insert(tls_ctx, NULL, el, fd,
298 NULL,
299 _ldap_start_tls_io_write, /* We'll be called again when the conn is open */
301 tls_ctx);
302 if (!fr_cond_assert(ret == 0)) goto error;
303 break;
304
305 case LDAP_SUCCESS:
306 if (fd < 0) {
307 ret = ldap_get_option(c->handle, LDAP_OPT_DESC, &fd);
308 if ((ret != LDAP_OPT_SUCCESS) || (fd < 0)) goto error;
309 }
310 c->fd = fd;
311
312 /*
313 * libldap, up to at least 2.7.2, switches the socket to blocking mode
314 * while sending the first request after an async connect.
315 */
316 if (fr_nonblock(fd) < 0) {
317 PERROR("Failed setting LDAP connection non-blocking");
318 goto error;
319 }
320
321 ret = fr_event_fd_insert(tls_ctx, NULL, el, fd,
323 NULL,
325 tls_ctx);
326 if (!fr_cond_assert(ret == 0)) goto error;
327 break;
328
329 default:
330 ERROR("ldap_start_tls failed: %s", ldap_err2string(ret));
331 goto error;
332 }
333}
334
335
336/** Install I/O handlers for Start TLS negotiation
337 *
338 * @param[in] c connection to StartTLS on.
339 * @param[in] serverctrls Extra controls to pass to the server.
340 * @param[in] clientctrls Extra controls to pass to libldap.
341 * @return
342 * - 0 on success.
343 * - -1 on failure.
344 */
345int fr_ldap_start_tls_async(fr_ldap_connection_t *c, LDAPControl **serverctrls, LDAPControl **clientctrls)
346{
347 int fd = -1;
350
351 DEBUG2("Starting TLS negotiation");
352
353 MEM(tls_ctx = talloc_zero(c, fr_ldap_start_tls_ctx_t));
354 tls_ctx->c = c;
355 tls_ctx->serverctrls = serverctrls;
356 tls_ctx->clientctrls = clientctrls;
357
358 el = c->conn->el;
359
360 /*
361 * ldap_get_option can return LDAP_SUCCESS even if the fd is not yet available
362 * - hence the test for fd >= 0
363 */
364 if ((ldap_get_option(c->handle, LDAP_OPT_DESC, &fd) == LDAP_SUCCESS) && (fd >= 0)) {
365 int ret;
366
367 ret = fr_event_fd_insert(tls_ctx, NULL, el, fd,
368 NULL,
371 tls_ctx);
372 if (!fr_cond_assert(ret == 0)) {
373 talloc_free(tls_ctx);
374 return -1;
375 }
376 } else {
377 _ldap_start_tls_io_write(el, -1, 0, tls_ctx);
378 }
379
380 return 0;
381}
#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 fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:194
#define MEM(x)
Definition debug.h:38
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define fr_event_fd_insert(...)
Definition event.h:247
talloc_free(hp)
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
fr_ldap_state_t fr_ldap_state_next(fr_ldap_connection_t *c)
Move between LDAP connection states.
Definition state.c:50
#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_PENDING
fr_ldap_result() was polling and hasn't read the final message yet.
Definition base.h:568
connection_t * conn
Connection state handle.
Definition base.h:339
Tracks the state of a libldap connection handle.
Definition base.h:326
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
Stores all information relating to an event list.
Definition event.c:375
int fr_nonblock(UNUSED int fd)
Definition misc.c:293
#define DEBUG2(fmt,...)
static void _ldap_start_tls_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 start_tls.c:50
static void ldap_start_tls_handshake(UNUSED fr_event_list_t *el, fr_ldap_start_tls_ctx_t *tls_ctx)
Run the TLS handshake to completion.
Definition start_tls.c:191
fr_ldap_connection_t * c
The current connection.
Definition start_tls.c:35
static void _ldap_start_tls_io_write(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
Send an extended operation to the LDAP server, requesting a transition to TLS.
Definition start_tls.c:265
int fr_ldap_start_tls_async(fr_ldap_connection_t *c, LDAPControl **serverctrls, LDAPControl **clientctrls)
Install I/O handlers for Start TLS negotiation.
Definition start_tls.c:345
LDAPControl ** serverctrls
Controls to pass to the server.
Definition start_tls.c:36
LDAPControl ** clientctrls
Controls to pass to the client (library).
Definition start_tls.c:37
static void _ldap_start_tls_io_read(fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
Event handler for the response to the StartTLS extended operation.
Definition start_tls.c:228
Holds arguments for the start_tls operation.
Definition start_tls.c:34
#define fr_time_delta_wrap(_time)
Definition time.h:152
static fr_event_list_t * el