The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
24 RCSID("$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  */
34 typedef 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  */
96 static 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) {
110  case LDAP_PROC_SUCCESS:
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  */
149 static 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  */
222 int fr_ldap_start_tls_async(fr_ldap_connection_t *c, LDAPControl **serverctrls, LDAPControl **clientctrls)
223 {
224  int fd = -1;
225  fr_ldap_start_tls_ctx_t *tls_ctx;
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: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
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
int fr_ldap_connection_timeout_set(fr_ldap_connection_t const *conn, fr_time_delta_t timeout)
Definition: connection.c:403
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
fr_time_delta_t tls_handshake_timeout
How long we wait for the TLS handshake to complete.
Definition: base.h:307
fr_ldap_config_t const * config
rlm_ldap connection configuration.
Definition: base.h:342
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:425
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
Tracks the state of a libldap connection handle.
Definition: base.h:330
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
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
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
#define fr_time_delta_wrap(_time)
Definition: time.h:152
static fr_event_list_t * el