The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
start_tls.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: 527ba041343b8d0c06061207fc2e4770af26371e $
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: 527ba041343b8d0c06061207fc2e4770af26371e $")
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/** Event handler for the response to the StartTLS extended operation
61 *
62 * Call flow is:
63 *
64 * - ldap_install_tls
65 * - calls ldap_pvt_tls_inplace to check is the Sockbuf for defconn has TLS installed
66 * - If it does (it shouldn't), returns LDAP_LOCAL_ERROR (and we fail).
67 * - calls ldap_int_tls_start.
68 * - calls tls_init (to initialise ssl library - only done once per implementation).
69 * - if net timeout is >= 0, then set the FD to nonblocking mode.
70 * - calls ldap_int_tls_connect
71 * - either gets existing session or
72 * - installs sockbuff shims to do tls encode/decode.
73 * - calls connect callback
74 * - calls ->ti_session_connect (ssl library callback)
75 * - calls tlso_session_connect (openssl shim)
76 * - calls SSL_connect - SSL_connect can be called multiple times
77 * to continue session negotiation.
78 * returns 0 on success, -1 on error.
79 * - on -1, calls update_flags, which calls tlso_session_upflags
80 * - calls SSL_get_error, which returns SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,
81 * SSL_ERROR_WANT_CONNECT, or another error. If error code is one of the above
82 * returns 1, else returns 0.
83 * Sets sb->sb_trans_needs_read or sb->sb_trans_needs_write.
84 * - if update_flags returns 1 ldap_int_tls_connect returns 1.
85 * - calls ldap_int_poll to check for errors.
86 * - returns LDAP_TIMEOUT if no data is available and we hit the timeout.
87 *
88 * So unfortunately ldap_install_tls is blocking... We need to send patches to OpenLDAP
89 * in order to fix that.
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 Connection config and handle.
95 */
96static void _ldap_start_tls_io_read(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, void *uctx)
97{
98 fr_ldap_start_tls_ctx_t *tls_ctx = talloc_get_type_abort(uctx, fr_ldap_start_tls_ctx_t);
99 fr_ldap_connection_t *c = tls_ctx->c;
100 int ret;
101 fr_ldap_rcode_t status;
102
103 /*
104 * We're I/O driven, if there's no data someone lied to us
105 */
106 status = fr_ldap_result(NULL, NULL, c, tls_ctx->msgid, LDAP_MSG_ALL, NULL, fr_time_delta_wrap(0));
107 talloc_free(tls_ctx); /* Free explicitly so we don't accumulate contexts */
108
109 switch (status) {
111 /*
112 * If tls_handshake_timeout is NULL ldap_install_tls
113 * will block forever.
114 */
116
117 /*
118 * This call will block for a maximum of tls_handshake_timeout.
119 * Patches to libldap are required to fix this.
120 */
121 ret = ldap_install_tls(c->handle);
123 if (ret != LDAP_SUCCESS) {
124 ERROR("ldap_install_tls failed: %s", ldap_err2string(ret));
125 fr_ldap_state_error(c); /* Restart the connection state machine */
126 }
127
128 fr_ldap_state_next(c); /* onto the next operation */
129 break;
130
131 default:
132 PERROR("StartTLS failed");
133 fr_ldap_state_error(c); /* Restart the connection state machine */
134 break;
135 }
136}
137
138/** Send an extended operation to the LDAP server, requesting a transition to TLS
139 *
140 * Behind the scenes ldap_start_tls calls:
141 *
142 * ldap_extended_operation(ld, LDAP_EXOP_START_TLS, NULL, serverctrls, clientctrls, msgidp);
143 *
144 * @param[in] el the event occurred in.
145 * @param[in] fd the event occurred on.
146 * @param[in] flags from kevent.
147 * @param[in] uctx Connection config and handle.
148 */
149static void _ldap_start_tls_io_write(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
150{
151 fr_ldap_start_tls_ctx_t *tls_ctx = talloc_get_type_abort(uctx, fr_ldap_start_tls_ctx_t);
152 fr_ldap_connection_t *c = tls_ctx->c;
153
154 int ret;
155
156 LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS];
157 LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS];
158
159 fr_ldap_control_merge(our_serverctrls, our_clientctrls,
160 NUM_ELEMENTS(our_serverctrls),
161 NUM_ELEMENTS(our_clientctrls),
162 c, tls_ctx->serverctrls, tls_ctx->clientctrls);
163
164 ret = ldap_start_tls(c->handle, our_serverctrls, our_clientctrls, &tls_ctx->msgid);
165 /*
166 * If the handle was not connected, this operation
167 * can return either LDAP_X_CONNECTING or LDAP_SUCCESS
168 * depending on how fast the connection came up
169 * and whether it was connectionless.
170 */
171 switch (ret) {
172 case LDAP_X_CONNECTING: /* Connection in progress - retry later */
173 ret = ldap_get_option(c->handle, LDAP_OPT_DESC, &fd);
174 if (!fr_cond_assert(ret == LDAP_OPT_SUCCESS)) {
175 error:
176 talloc_free(tls_ctx);
178 fr_ldap_state_error(c); /* Restart the connection state machine */
179 return;
180 }
181
182 ret = fr_event_fd_insert(tls_ctx, NULL, el, fd,
183 NULL,
184 _ldap_start_tls_io_write, /* We'll be called again when the conn is open */
186 tls_ctx);
187 if (!fr_cond_assert(ret == 0)) goto error;
188 break;
189
190 case LDAP_SUCCESS:
191 if (fd < 0) {
192 ret = ldap_get_option(c->handle, LDAP_OPT_DESC, &fd);
193 if ((ret != LDAP_OPT_SUCCESS) || (fd < 0)) goto error;
194 }
195 c->fd = fd;
196 ret = fr_event_fd_insert(tls_ctx, NULL, el, fd,
198 NULL,
200 tls_ctx);
201 if (!fr_cond_assert(ret == 0)) goto error;
202 break;
203
204 default:
205 ERROR("ldap_start_tls failed: %s", ldap_err2string(ret));
206 goto error;
207 }
208
210}
211
212
213/** Install I/O handlers for Start TLS negotiation
214 *
215 * @param[in] c connection to StartTLS on.
216 * @param[in] serverctrls Extra controls to pass to the server.
217 * @param[in] clientctrls Extra controls to pass to libldap.
218 * @return
219 * - 0 on success.
220 * - -1 on failure.
221 */
222int fr_ldap_start_tls_async(fr_ldap_connection_t *c, LDAPControl **serverctrls, LDAPControl **clientctrls)
223{
224 int fd = -1;
227
228 DEBUG2("Starting TLS negotiation");
229
230 MEM(tls_ctx = talloc_zero(c, fr_ldap_start_tls_ctx_t));
231 tls_ctx->c = c;
232 tls_ctx->serverctrls = serverctrls;
233 tls_ctx->clientctrls = clientctrls;
234
235 el = c->conn->el;
236
237 /*
238 * ldap_get_option can return LDAP_SUCCESS even if the fd is not yet available
239 * - hence the test for fd >= 0
240 */
241 if ((ldap_get_option(c->handle, LDAP_OPT_DESC, &fd) == LDAP_SUCCESS) && (fd >= 0)) {
242 int ret;
243
244 ret = fr_event_fd_insert(tls_ctx, NULL, el, fd,
245 NULL,
248 tls_ctx);
249 if (!fr_cond_assert(ret == 0)) {
250 talloc_free(tls_ctx);
251 return -1;
252 }
253 } else {
254 _ldap_start_tls_io_write(el, -1, 0, tls_ctx);
255 }
256
257 return 0;
258}
#define USES_APPLE_DEPRECATED_API
Definition build.h:470
#define RCSID(id)
Definition build.h:483
#define UNUSED
Definition build.h:315
#define NUM_ELEMENTS(_t)
Definition build.h:337
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:139
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define fr_event_fd_insert(...)
Definition event.h:232
LDAP * handle
libldap handle.
Definition base.h:333
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 fr_ldap_connection_timeout_set(fr_ldap_connection_t const *conn, fr_time_delta_t timeout)
Definition connection.c:409
int fd
File descriptor for this connection.
Definition base.h:349
void fr_ldap_state_error(fr_ldap_connection_t *c)
Signal that there's been an error on the connection.
Definition state.c:134
fr_time_delta_t tls_handshake_timeout
How long we wait for the TLS handshake to complete.
Definition base.h:309
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition base.h:344
fr_ldap_state_t fr_ldap_state_next(fr_ldap_connection_t *c)
Move between LDAP connection states.
Definition state.c:49
#define LDAP_MAX_CONTROLS
Maximum number of client/server controls.
Definition base.h:94
int fr_ldap_connection_timeout_reset(fr_ldap_connection_t const *conn)
Definition connection.c:431
connection_t * conn
Connection state handle.
Definition base.h:345
fr_ldap_rcode_t
Codes returned by fr_ldap internal functions.
Definition base.h:582
@ LDAP_PROC_SUCCESS
Operation was successful.
Definition base.h:585
Tracks the state of a libldap connection handle.
Definition base.h:332
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:450
#define PERROR(_fmt,...)
Definition log.h:228
talloc_free(reap)
Stores all information relating to an event list.
Definition event.c:411
#define DEBUG2(fmt,...)
Definition radclient.h:43
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_io_read(UNUSED 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:96
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:149
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:222
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
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