1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 * Copyright (C) Howard Chu, <hyc@openldap.org>
10 *
11 * This software is licensed as described in the file COPYING, which
12 * you should have received as part of this distribution. The terms
13 * are also available at https://curl.se/docs/copyright.html.
14 *
15 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16 * copies of the Software, and permit persons to whom the Software is
17 * furnished to do so, under the terms of the COPYING file.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 * SPDX-License-Identifier: curl
23 *
24 ***************************************************************************/
25
26 #include "curl_setup.h"
27
28 #if !defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)
29
30 /*
31 * Notice that USE_OPENLDAP is only a source code selection switch. When
32 * libcurl is built with USE_OPENLDAP defined the libcurl source code that
33 * gets compiled is the code from openldap.c, otherwise the code that gets
34 * compiled is the code from ldap.c.
35 *
36 * When USE_OPENLDAP is defined a recent version of the OpenLDAP library
37 * might be required for compilation and runtime. In order to use ancient
38 * OpenLDAP library versions, USE_OPENLDAP shall not be defined.
39 */
40
41 #include <ldap.h>
42
43 #include "urldata.h"
44 #include <curl/curl.h>
45 #include "sendf.h"
46 #include "vtls/vtls.h"
47 #include "transfer.h"
48 #include "curl_ldap.h"
49 #include "curl_base64.h"
50 #include "cfilters.h"
51 #include "connect.h"
52 #include "curl_sasl.h"
53 #include "strcase.h"
54 /* The last 3 #include files should be in this order */
55 #include "curl_printf.h"
56 #include "curl_memory.h"
57 #include "memdebug.h"
58
59 /*
60 * Uncommenting this will enable the built-in debug logging of the openldap
61 * library. The debug log level can be set using the CURL_OPENLDAP_TRACE
62 * environment variable. The debug output is written to stderr.
63 *
64 * The library supports the following debug flags:
65 * LDAP_DEBUG_NONE 0x0000
66 * LDAP_DEBUG_TRACE 0x0001
67 * LDAP_DEBUG_CONSTRUCT 0x0002
68 * LDAP_DEBUG_DESTROY 0x0004
69 * LDAP_DEBUG_PARAMETER 0x0008
70 * LDAP_DEBUG_ANY 0xffff
71 *
72 * For example, use CURL_OPENLDAP_TRACE=0 for no debug,
73 * CURL_OPENLDAP_TRACE=2 for LDAP_DEBUG_CONSTRUCT messages only,
74 * CURL_OPENLDAP_TRACE=65535 for all debug message levels.
75 */
76 /* #define CURL_OPENLDAP_DEBUG */
77
78 /* Machine states. */
79 typedef enum {
80 OLDAP_STOP, /* Do nothing state, stops the state machine */
81 OLDAP_SSL, /* Performing SSL handshake. */
82 OLDAP_STARTTLS, /* STARTTLS request sent. */
83 OLDAP_TLS, /* Performing TLS handshake. */
84 OLDAP_MECHS, /* Get SASL authentication mechanisms. */
85 OLDAP_SASL, /* SASL binding reply. */
86 OLDAP_BIND, /* Simple bind reply. */
87 OLDAP_BINDV2, /* Simple bind reply in protocol version 2. */
88 OLDAP_LAST /* Never used */
89 } ldapstate;
90
91 #ifndef _LDAP_PVT_H
92 extern int ldap_pvt_url_scheme2proto(const char *);
93 extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url,
94 LDAP **ld);
95 #endif
96
97 static CURLcode oldap_setup_connection(struct Curl_easy *data,
98 struct connectdata *conn);
99 static CURLcode oldap_do(struct Curl_easy *data, bool *done);
100 static CURLcode oldap_done(struct Curl_easy *data, CURLcode, bool);
101 static CURLcode oldap_connect(struct Curl_easy *data, bool *done);
102 static CURLcode oldap_connecting(struct Curl_easy *data, bool *done);
103 static CURLcode oldap_disconnect(struct Curl_easy *data,
104 struct connectdata *conn, bool dead);
105
106 static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
107 const struct bufref *initresp);
108 static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
109 const struct bufref *resp);
110 static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech);
111 static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out);
112
113 static Curl_recv oldap_recv;
114
115 /*
116 * LDAP protocol handler.
117 */
118
119 const struct Curl_handler Curl_handler_ldap = {
120 "ldap", /* scheme */
121 oldap_setup_connection, /* setup_connection */
122 oldap_do, /* do_it */
123 oldap_done, /* done */
124 ZERO_NULL, /* do_more */
125 oldap_connect, /* connect_it */
126 oldap_connecting, /* connecting */
127 ZERO_NULL, /* doing */
128 ZERO_NULL, /* proto_getsock */
129 ZERO_NULL, /* doing_getsock */
130 ZERO_NULL, /* domore_getsock */
131 ZERO_NULL, /* perform_getsock */
132 oldap_disconnect, /* disconnect */
133 ZERO_NULL, /* write_resp */
134 ZERO_NULL, /* write_resp_hd */
135 ZERO_NULL, /* connection_check */
136 ZERO_NULL, /* attach connection */
137 PORT_LDAP, /* defport */
138 CURLPROTO_LDAP, /* protocol */
139 CURLPROTO_LDAP, /* family */
140 PROTOPT_NONE /* flags */
141 };
142
143 #ifdef USE_SSL
144 /*
145 * LDAPS protocol handler.
146 */
147
148 const struct Curl_handler Curl_handler_ldaps = {
149 "ldaps", /* scheme */
150 oldap_setup_connection, /* setup_connection */
151 oldap_do, /* do_it */
152 oldap_done, /* done */
153 ZERO_NULL, /* do_more */
154 oldap_connect, /* connect_it */
155 oldap_connecting, /* connecting */
156 ZERO_NULL, /* doing */
157 ZERO_NULL, /* proto_getsock */
158 ZERO_NULL, /* doing_getsock */
159 ZERO_NULL, /* domore_getsock */
160 ZERO_NULL, /* perform_getsock */
161 oldap_disconnect, /* disconnect */
162 ZERO_NULL, /* write_resp */
163 ZERO_NULL, /* write_resp_hd */
164 ZERO_NULL, /* connection_check */
165 ZERO_NULL, /* attach connection */
166 PORT_LDAPS, /* defport */
167 CURLPROTO_LDAPS, /* protocol */
168 CURLPROTO_LDAP, /* family */
169 PROTOPT_SSL /* flags */
170 };
171 #endif
172
173 /* SASL parameters for the ldap protocol */
174 static const struct SASLproto saslldap = {
175 "ldap", /* The service name */
176 oldap_perform_auth, /* Send authentication command */
177 oldap_continue_auth, /* Send authentication continuation */
178 oldap_cancel_auth, /* Send authentication cancellation */
179 oldap_get_message, /* Get SASL response message */
180 0, /* Maximum initial response length (no max) */
181 LDAP_SASL_BIND_IN_PROGRESS, /* Code received when continuation is expected */
182 LDAP_SUCCESS, /* Code to receive upon authentication success */
183 SASL_AUTH_NONE, /* Default mechanisms */
184 0 /* Configuration flags */
185 };
186
187 struct ldapconninfo {
188 struct SASL sasl; /* SASL-related parameters */
189 LDAP *ld; /* Openldap connection handle. */
190 Curl_recv *recv; /* For stacking SSL handler */
191 Curl_send *send;
192 struct berval *servercred; /* SASL data from server. */
193 ldapstate state; /* Current machine state. */
194 int proto; /* LDAP_PROTO_TCP/LDAP_PROTO_UDP/LDAP_PROTO_IPC */
195 int msgid; /* Current message id. */
196 };
197
198 struct ldapreqinfo {
199 int msgid;
200 int nument;
201 };
202
203 /*
204 * oldap_state()
205 *
206 * This is the ONLY way to change LDAP state!
207 */
oldap_state(struct Curl_easy * data,ldapstate newstate)208 static void oldap_state(struct Curl_easy *data, ldapstate newstate)
209 {
210 struct ldapconninfo *ldapc = data->conn->proto.ldapc;
211
212 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
213 /* for debug purposes */
214 static const char * const names[] = {
215 "STOP",
216 "SSL",
217 "STARTTLS",
218 "TLS",
219 "MECHS",
220 "SASL",
221 "BIND",
222 "BINDV2",
223 /* LAST */
224 };
225
226 if(ldapc->state != newstate)
227 infof(data, "LDAP %p state change from %s to %s",
228 (void *)ldapc, names[ldapc->state], names[newstate]);
229 #endif
230
231 ldapc->state = newstate;
232 }
233
234 /* Map some particular LDAP error codes to CURLcode values. */
oldap_map_error(int rc,CURLcode result)235 static CURLcode oldap_map_error(int rc, CURLcode result)
236 {
237 switch(rc) {
238 case LDAP_NO_MEMORY:
239 return CURLE_OUT_OF_MEMORY;
240 case LDAP_INVALID_CREDENTIALS:
241 return CURLE_LOGIN_DENIED;
242 case LDAP_PROTOCOL_ERROR:
243 return CURLE_UNSUPPORTED_PROTOCOL;
244 case LDAP_INSUFFICIENT_ACCESS:
245 return CURLE_REMOTE_ACCESS_DENIED;
246 }
247 return result;
248 }
249
oldap_url_parse(struct Curl_easy * data,LDAPURLDesc ** ludp)250 static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
251 {
252 CURLcode result = CURLE_OK;
253 int rc = LDAP_URL_ERR_BADURL;
254 static const char * const url_errs[] = {
255 "success",
256 "out of memory",
257 "bad parameter",
258 "unrecognized scheme",
259 "unbalanced delimiter",
260 "bad URL",
261 "bad host or port",
262 "bad or missing attributes",
263 "bad or missing scope",
264 "bad or missing filter",
265 "bad or missing extensions"
266 };
267
268 *ludp = NULL;
269 if(!data->state.up.user && !data->state.up.password &&
270 !data->state.up.options)
271 rc = ldap_url_parse(data->state.url, ludp);
272 if(rc != LDAP_URL_SUCCESS) {
273 const char *msg = "url parsing problem";
274
275 result = rc == LDAP_URL_ERR_MEM ? CURLE_OUT_OF_MEMORY :
276 CURLE_URL_MALFORMAT;
277 rc -= LDAP_URL_SUCCESS;
278 if((size_t) rc < sizeof(url_errs) / sizeof(url_errs[0]))
279 msg = url_errs[rc];
280 failf(data, "LDAP local: %s", msg);
281 }
282 return result;
283 }
284
285 /* Parse the login options. */
oldap_parse_login_options(struct connectdata * conn)286 static CURLcode oldap_parse_login_options(struct connectdata *conn)
287 {
288 CURLcode result = CURLE_OK;
289 struct ldapconninfo *li = conn->proto.ldapc;
290 const char *ptr = conn->options;
291
292 while(!result && ptr && *ptr) {
293 const char *key = ptr;
294 const char *value;
295
296 while(*ptr && *ptr != '=')
297 ptr++;
298
299 value = ptr + 1;
300
301 while(*ptr && *ptr != ';')
302 ptr++;
303
304 if(checkprefix("AUTH=", key))
305 result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value);
306 else
307 result = CURLE_SETOPT_OPTION_SYNTAX;
308
309 if(*ptr == ';')
310 ptr++;
311 }
312
313 return result == CURLE_URL_MALFORMAT ? CURLE_SETOPT_OPTION_SYNTAX : result;
314 }
315
oldap_setup_connection(struct Curl_easy * data,struct connectdata * conn)316 static CURLcode oldap_setup_connection(struct Curl_easy *data,
317 struct connectdata *conn)
318 {
319 CURLcode result;
320 LDAPURLDesc *lud;
321 (void)conn;
322
323 /* Early URL syntax check. */
324 result = oldap_url_parse(data, &lud);
325 ldap_free_urldesc(lud);
326
327 return result;
328 }
329
330 /*
331 * Get the SASL authentication challenge from the server credential buffer.
332 */
oldap_get_message(struct Curl_easy * data,struct bufref * out)333 static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out)
334 {
335 struct berval *servercred = data->conn->proto.ldapc->servercred;
336
337 if(!servercred || !servercred->bv_val)
338 return CURLE_WEIRD_SERVER_REPLY;
339 Curl_bufref_set(out, servercred->bv_val, servercred->bv_len, NULL);
340 return CURLE_OK;
341 }
342
343 /*
344 * Sends an initial SASL bind request to the server.
345 */
oldap_perform_auth(struct Curl_easy * data,const char * mech,const struct bufref * initresp)346 static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
347 const struct bufref *initresp)
348 {
349 struct connectdata *conn = data->conn;
350 struct ldapconninfo *li = conn->proto.ldapc;
351 struct berval cred;
352 struct berval *pcred = &cred;
353 int rc;
354
355 cred.bv_val = (char *) Curl_bufref_ptr(initresp);
356 cred.bv_len = Curl_bufref_len(initresp);
357 if(!cred.bv_val)
358 pcred = NULL;
359 rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid);
360 if(rc != LDAP_SUCCESS)
361 return oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
362 return CURLE_OK;
363 }
364
365 /*
366 * Sends SASL continuation.
367 */
oldap_continue_auth(struct Curl_easy * data,const char * mech,const struct bufref * resp)368 static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
369 const struct bufref *resp)
370 {
371 struct connectdata *conn = data->conn;
372 struct ldapconninfo *li = conn->proto.ldapc;
373 struct berval cred;
374 struct berval *pcred = &cred;
375 int rc;
376
377 cred.bv_val = (char *) Curl_bufref_ptr(resp);
378 cred.bv_len = Curl_bufref_len(resp);
379 if(!cred.bv_val)
380 pcred = NULL;
381 rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid);
382 if(rc != LDAP_SUCCESS)
383 return oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
384 return CURLE_OK;
385 }
386
387 /*
388 * Sends SASL bind cancellation.
389 */
oldap_cancel_auth(struct Curl_easy * data,const char * mech)390 static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech)
391 {
392 struct ldapconninfo *li = data->conn->proto.ldapc;
393 int rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL,
394 &li->msgid);
395
396 (void)mech;
397 if(rc != LDAP_SUCCESS)
398 return oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
399 return CURLE_OK;
400 }
401
402 /* Starts LDAP simple bind. */
oldap_perform_bind(struct Curl_easy * data,ldapstate newstate)403 static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate)
404 {
405 struct connectdata *conn = data->conn;
406 struct ldapconninfo *li = conn->proto.ldapc;
407 char *binddn = NULL;
408 struct berval passwd;
409 int rc;
410
411 passwd.bv_val = NULL;
412 passwd.bv_len = 0;
413
414 if(data->state.aptr.user) {
415 binddn = conn->user;
416 passwd.bv_val = conn->passwd;
417 passwd.bv_len = strlen(passwd.bv_val);
418 }
419
420 rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
421 NULL, NULL, &li->msgid);
422 if(rc != LDAP_SUCCESS)
423 return oldap_map_error(rc,
424 data->state.aptr.user ?
425 CURLE_LOGIN_DENIED : CURLE_LDAP_CANNOT_BIND);
426 oldap_state(data, newstate);
427 return CURLE_OK;
428 }
429
430 /* Query the supported SASL authentication mechanisms. */
oldap_perform_mechs(struct Curl_easy * data)431 static CURLcode oldap_perform_mechs(struct Curl_easy *data)
432 {
433 struct ldapconninfo *li = data->conn->proto.ldapc;
434 int rc;
435 static const char * const supportedSASLMechanisms[] = {
436 "supportedSASLMechanisms",
437 NULL
438 };
439
440 rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
441 (char **) supportedSASLMechanisms, 0,
442 NULL, NULL, NULL, 0, &li->msgid);
443 if(rc != LDAP_SUCCESS)
444 return oldap_map_error(rc, CURLE_LOGIN_DENIED);
445 oldap_state(data, OLDAP_MECHS);
446 return CURLE_OK;
447 }
448
449 /* Starts SASL bind. */
oldap_perform_sasl(struct Curl_easy * data)450 static CURLcode oldap_perform_sasl(struct Curl_easy *data)
451 {
452 saslprogress progress = SASL_IDLE;
453 struct ldapconninfo *li = data->conn->proto.ldapc;
454 CURLcode result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
455
456 oldap_state(data, OLDAP_SASL);
457 if(!result && progress != SASL_INPROGRESS)
458 result = CURLE_LOGIN_DENIED;
459 return result;
460 }
461
462 #ifdef USE_SSL
463 static Sockbuf_IO ldapsb_tls;
464
ssl_installed(struct connectdata * conn)465 static bool ssl_installed(struct connectdata *conn)
466 {
467 return conn->proto.ldapc->recv != NULL;
468 }
469
oldap_ssl_connect(struct Curl_easy * data,ldapstate newstate)470 static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
471 {
472 struct connectdata *conn = data->conn;
473 struct ldapconninfo *li = conn->proto.ldapc;
474 bool ssldone = FALSE;
475 CURLcode result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
476 if(!result) {
477 oldap_state(data, newstate);
478
479 if(ssldone) {
480 Sockbuf *sb;
481
482 /* Install the libcurl SSL handlers into the sockbuf. */
483 ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
484 ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
485 li->recv = conn->recv[FIRSTSOCKET];
486 li->send = conn->send[FIRSTSOCKET];
487 }
488 }
489
490 return result;
491 }
492
493 /* Send the STARTTLS request */
oldap_perform_starttls(struct Curl_easy * data)494 static CURLcode oldap_perform_starttls(struct Curl_easy *data)
495 {
496 struct ldapconninfo *li = data->conn->proto.ldapc;
497 int rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
498
499 if(rc != LDAP_SUCCESS)
500 return oldap_map_error(rc, CURLE_USE_SSL_FAILED);
501 oldap_state(data, OLDAP_STARTTLS);
502 return CURLE_OK;
503 }
504 #endif
505
oldap_connect(struct Curl_easy * data,bool * done)506 static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
507 {
508 struct connectdata *conn = data->conn;
509 struct ldapconninfo *li;
510 static const int version = LDAP_VERSION3;
511 int rc;
512 char *hosturl;
513 #ifdef CURL_OPENLDAP_DEBUG
514 static int do_trace = -1;
515 #endif
516
517 (void)done;
518
519 DEBUGASSERT(!conn->proto.ldapc);
520 li = calloc(1, sizeof(struct ldapconninfo));
521 if(!li)
522 return CURLE_OUT_OF_MEMORY;
523 else {
524 CURLcode result;
525 li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
526 conn->proto.ldapc = li;
527
528 /* Initialize the SASL storage */
529 Curl_sasl_init(&li->sasl, data, &saslldap);
530
531 /* Clear the TLS upgraded flag */
532 conn->bits.tls_upgraded = FALSE;
533
534 result = oldap_parse_login_options(conn);
535 if(result)
536 return result;
537 }
538
539 hosturl = aprintf("%s://%s%s%s:%d",
540 conn->handler->scheme,
541 conn->bits.ipv6_ip ? "[" : "",
542 conn->host.name,
543 conn->bits.ipv6_ip ? "]" : "",
544 conn->remote_port);
545 if(!hosturl)
546 return CURLE_OUT_OF_MEMORY;
547
548 rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
549 if(rc) {
550 failf(data, "LDAP local: Cannot connect to %s, %s",
551 hosturl, ldap_err2string(rc));
552 free(hosturl);
553 return CURLE_COULDNT_CONNECT;
554 }
555
556 free(hosturl);
557
558 #ifdef CURL_OPENLDAP_DEBUG
559 if(do_trace < 0) {
560 const char *env = getenv("CURL_OPENLDAP_TRACE");
561 do_trace = (env && strtol(env, NULL, 10) > 0);
562 }
563 if(do_trace)
564 ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
565 #endif
566
567 /* Try version 3 first. */
568 ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
569
570 /* Do not chase referrals. */
571 ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
572
573 #ifdef USE_SSL
574 if(conn->handler->flags & PROTOPT_SSL)
575 return oldap_ssl_connect(data, OLDAP_SSL);
576
577 if(data->set.use_ssl) {
578 CURLcode result = oldap_perform_starttls(data);
579
580 if(!result || data->set.use_ssl != CURLUSESSL_TRY)
581 return result;
582 }
583 #endif
584
585 if(li->sasl.prefmech != SASL_AUTH_NONE)
586 return oldap_perform_mechs(data);
587
588 /* Force bind even if anonymous bind is not needed in protocol version 3
589 to detect missing version 3 support. */
590 return oldap_perform_bind(data, OLDAP_BIND);
591 }
592
593 /* Handle the supported SASL mechanisms query response */
oldap_state_mechs_resp(struct Curl_easy * data,LDAPMessage * msg,int code)594 static CURLcode oldap_state_mechs_resp(struct Curl_easy *data,
595 LDAPMessage *msg, int code)
596 {
597 struct connectdata *conn = data->conn;
598 struct ldapconninfo *li = conn->proto.ldapc;
599 int rc;
600 BerElement *ber = NULL;
601 CURLcode result = CURLE_OK;
602 struct berval bv, *bvals;
603
604 switch(ldap_msgtype(msg)) {
605 case LDAP_RES_SEARCH_ENTRY:
606 /* Got a list of supported SASL mechanisms. */
607 if(code != LDAP_SUCCESS && code != LDAP_NO_RESULTS_RETURNED)
608 return CURLE_LOGIN_DENIED;
609
610 rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
611 if(rc < 0)
612 return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING);
613 for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
614 rc == LDAP_SUCCESS;
615 rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
616 int i;
617
618 if(!bv.bv_val)
619 break;
620
621 if(bvals) {
622 for(i = 0; bvals[i].bv_val; i++) {
623 size_t llen;
624 unsigned short mech = Curl_sasl_decode_mech((char *) bvals[i].bv_val,
625 bvals[i].bv_len, &llen);
626 if(bvals[i].bv_len == llen)
627 li->sasl.authmechs |= mech;
628 }
629 ber_memfree(bvals);
630 }
631 }
632 ber_free(ber, 0);
633 break;
634
635 case LDAP_RES_SEARCH_RESULT:
636 switch(code) {
637 case LDAP_SIZELIMIT_EXCEEDED:
638 infof(data, "Too many authentication mechanisms\n");
639 FALLTHROUGH();
640 case LDAP_SUCCESS:
641 case LDAP_NO_RESULTS_RETURNED:
642 if(Curl_sasl_can_authenticate(&li->sasl, data))
643 result = oldap_perform_sasl(data);
644 else
645 result = CURLE_LOGIN_DENIED;
646 break;
647 default:
648 result = oldap_map_error(code, CURLE_LOGIN_DENIED);
649 break;
650 }
651 break;
652 default:
653 break;
654 }
655 return result;
656 }
657
658 /* Handle a SASL bind response. */
oldap_state_sasl_resp(struct Curl_easy * data,LDAPMessage * msg,int code)659 static CURLcode oldap_state_sasl_resp(struct Curl_easy *data,
660 LDAPMessage *msg, int code)
661 {
662 struct connectdata *conn = data->conn;
663 struct ldapconninfo *li = conn->proto.ldapc;
664 CURLcode result = CURLE_OK;
665 saslprogress progress;
666 int rc;
667
668 li->servercred = NULL;
669 rc = ldap_parse_sasl_bind_result(li->ld, msg, &li->servercred, 0);
670 if(rc != LDAP_SUCCESS) {
671 failf(data, "LDAP local: sasl ldap_parse_result %s", ldap_err2string(rc));
672 result = oldap_map_error(rc, CURLE_LOGIN_DENIED);
673 }
674 else {
675 result = Curl_sasl_continue(&li->sasl, data, code, &progress);
676 if(!result && progress != SASL_INPROGRESS)
677 oldap_state(data, OLDAP_STOP);
678 }
679
680 if(li->servercred)
681 ber_bvfree(li->servercred);
682 return result;
683 }
684
685 /* Handle a simple bind response. */
oldap_state_bind_resp(struct Curl_easy * data,LDAPMessage * msg,int code)686 static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg,
687 int code)
688 {
689 struct connectdata *conn = data->conn;
690 struct ldapconninfo *li = conn->proto.ldapc;
691 CURLcode result = CURLE_OK;
692 struct berval *bv = NULL;
693 int rc;
694
695 if(code != LDAP_SUCCESS)
696 return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND);
697
698 rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0);
699 if(rc != LDAP_SUCCESS) {
700 failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s",
701 ldap_err2string(rc));
702 result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
703 }
704 else
705 oldap_state(data, OLDAP_STOP);
706
707 if(bv)
708 ber_bvfree(bv);
709 return result;
710 }
711
oldap_connecting(struct Curl_easy * data,bool * done)712 static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
713 {
714 CURLcode result = CURLE_OK;
715 struct connectdata *conn = data->conn;
716 struct ldapconninfo *li = conn->proto.ldapc;
717 LDAPMessage *msg = NULL;
718 struct timeval tv = {0, 0};
719 int code = LDAP_SUCCESS;
720 int rc;
721
722 if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) {
723 /* Get response to last command. */
724 rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg);
725 switch(rc) {
726 case 0: /* Timed out. */
727 return CURLE_OK;
728 case LDAP_RES_SEARCH_ENTRY:
729 case LDAP_RES_SEARCH_REFERENCE:
730 break;
731 default:
732 li->msgid = 0; /* Nothing to abandon upon error. */
733 if(rc < 0) {
734 failf(data, "LDAP local: connecting ldap_result %s",
735 ldap_err2string(rc));
736 return oldap_map_error(rc, CURLE_COULDNT_CONNECT);
737 }
738 break;
739 }
740
741 /* Get error code from message. */
742 rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0);
743 if(rc)
744 code = rc;
745 else {
746 /* store the latest code for later retrieval */
747 data->info.httpcode = code;
748 }
749
750 /* If protocol version 3 is not supported, fallback to version 2. */
751 if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 &&
752 #ifdef USE_SSL
753 (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) &&
754 #endif
755 li->sasl.prefmech == SASL_AUTH_NONE) {
756 static const int version = LDAP_VERSION2;
757
758 ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
759 ldap_msgfree(msg);
760 return oldap_perform_bind(data, OLDAP_BINDV2);
761 }
762 }
763
764 /* Handle response message according to current state. */
765 switch(li->state) {
766
767 #ifdef USE_SSL
768 case OLDAP_SSL:
769 result = oldap_ssl_connect(data, OLDAP_SSL);
770 if(!result && ssl_installed(conn)) {
771 if(li->sasl.prefmech != SASL_AUTH_NONE)
772 result = oldap_perform_mechs(data);
773 else
774 result = oldap_perform_bind(data, OLDAP_BIND);
775 }
776 break;
777 case OLDAP_STARTTLS:
778 if(code != LDAP_SUCCESS) {
779 if(data->set.use_ssl != CURLUSESSL_TRY)
780 result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
781 else if(li->sasl.prefmech != SASL_AUTH_NONE)
782 result = oldap_perform_mechs(data);
783 else
784 result = oldap_perform_bind(data, OLDAP_BIND);
785 break;
786 }
787 result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
788 if(result)
789 break;
790 FALLTHROUGH();
791 case OLDAP_TLS:
792 result = oldap_ssl_connect(data, OLDAP_TLS);
793 if(result)
794 result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
795 else if(ssl_installed(conn)) {
796 conn->bits.tls_upgraded = TRUE;
797 if(li->sasl.prefmech != SASL_AUTH_NONE)
798 result = oldap_perform_mechs(data);
799 else if(data->state.aptr.user)
800 result = oldap_perform_bind(data, OLDAP_BIND);
801 else {
802 /* Version 3 supported: no bind required */
803 oldap_state(data, OLDAP_STOP);
804 result = CURLE_OK;
805 }
806 }
807 break;
808 #endif
809
810 case OLDAP_MECHS:
811 result = oldap_state_mechs_resp(data, msg, code);
812 break;
813 case OLDAP_SASL:
814 result = oldap_state_sasl_resp(data, msg, code);
815 break;
816 case OLDAP_BIND:
817 case OLDAP_BINDV2:
818 result = oldap_state_bind_resp(data, msg, code);
819 break;
820 default:
821 /* internal error */
822 result = CURLE_COULDNT_CONNECT;
823 break;
824 }
825
826 ldap_msgfree(msg);
827
828 *done = li->state == OLDAP_STOP;
829 if(*done)
830 conn->recv[FIRSTSOCKET] = oldap_recv;
831
832 if(result && li->msgid) {
833 ldap_abandon_ext(li->ld, li->msgid, NULL, NULL);
834 li->msgid = 0;
835 }
836 return result;
837 }
838
oldap_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead_connection)839 static CURLcode oldap_disconnect(struct Curl_easy *data,
840 struct connectdata *conn,
841 bool dead_connection)
842 {
843 struct ldapconninfo *li = conn->proto.ldapc;
844 (void) dead_connection;
845 #ifndef USE_SSL
846 (void)data;
847 #endif
848
849 if(li) {
850 if(li->ld) {
851 #ifdef USE_SSL
852 if(ssl_installed(conn)) {
853 Sockbuf *sb;
854 ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
855 ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
856 }
857 #endif
858 ldap_unbind_ext(li->ld, NULL, NULL);
859 li->ld = NULL;
860 }
861 Curl_sasl_cleanup(conn, li->sasl.authused);
862 conn->proto.ldapc = NULL;
863 free(li);
864 }
865 return CURLE_OK;
866 }
867
oldap_do(struct Curl_easy * data,bool * done)868 static CURLcode oldap_do(struct Curl_easy *data, bool *done)
869 {
870 struct connectdata *conn = data->conn;
871 struct ldapconninfo *li = conn->proto.ldapc;
872 struct ldapreqinfo *lr;
873 CURLcode result;
874 int rc;
875 LDAPURLDesc *lud;
876 int msgid;
877
878 connkeep(conn, "OpenLDAP do");
879
880 infof(data, "LDAP local: %s", data->state.url);
881
882 result = oldap_url_parse(data, &lud);
883 if(!result) {
884 #ifdef USE_SSL
885 if(ssl_installed(conn)) {
886 Sockbuf *sb;
887 /* re-install the libcurl SSL handlers into the sockbuf. */
888 ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
889 ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
890 }
891 #endif
892
893 rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
894 lud->lud_filter, lud->lud_attrs, 0,
895 NULL, NULL, NULL, 0, &msgid);
896 ldap_free_urldesc(lud);
897 if(rc != LDAP_SUCCESS) {
898 failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
899 result = CURLE_LDAP_SEARCH_FAILED;
900 }
901 else {
902 lr = calloc(1, sizeof(struct ldapreqinfo));
903 if(!lr) {
904 ldap_abandon_ext(li->ld, msgid, NULL, NULL);
905 result = CURLE_OUT_OF_MEMORY;
906 }
907 else {
908 lr->msgid = msgid;
909 data->req.p.ldap = lr;
910 Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
911 *done = TRUE;
912 }
913 }
914 }
915 return result;
916 }
917
oldap_done(struct Curl_easy * data,CURLcode res,bool premature)918 static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
919 bool premature)
920 {
921 struct connectdata *conn = data->conn;
922 struct ldapreqinfo *lr = data->req.p.ldap;
923
924 (void)res;
925 (void)premature;
926
927 if(lr) {
928 /* if there was a search in progress, abandon it */
929 if(lr->msgid) {
930 struct ldapconninfo *li = conn->proto.ldapc;
931 ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
932 lr->msgid = 0;
933 }
934 data->req.p.ldap = NULL;
935 free(lr);
936 }
937
938 return CURLE_OK;
939 }
940
client_write(struct Curl_easy * data,const char * prefix,size_t plen,const char * value,size_t len,const char * suffix,size_t slen)941 static CURLcode client_write(struct Curl_easy *data,
942 const char *prefix, size_t plen,
943 const char *value, size_t len,
944 const char *suffix, size_t slen)
945 {
946 CURLcode result = CURLE_OK;
947
948 if(prefix) {
949 /* If we have a zero-length value and the prefix ends with a space
950 separator, drop the latter. */
951 if(!len && plen && prefix[plen - 1] == ' ')
952 plen--;
953 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen);
954 }
955 if(!result && value) {
956 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len);
957 }
958 if(!result && suffix) {
959 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen);
960 }
961 return result;
962 }
963
oldap_recv(struct Curl_easy * data,int sockindex,char * buf,size_t len,CURLcode * err)964 static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
965 size_t len, CURLcode *err)
966 {
967 struct connectdata *conn = data->conn;
968 struct ldapconninfo *li = conn->proto.ldapc;
969 struct ldapreqinfo *lr = data->req.p.ldap;
970 int rc;
971 LDAPMessage *msg = NULL;
972 BerElement *ber = NULL;
973 struct timeval tv = {0, 0};
974 struct berval bv, *bvals;
975 bool binary = FALSE;
976 CURLcode result = CURLE_AGAIN;
977 int code;
978 char *info = NULL;
979
980 (void)len;
981 (void)buf;
982 (void)sockindex;
983
984 rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
985 if(rc < 0) {
986 failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
987 result = CURLE_RECV_ERROR;
988 }
989
990 *err = result;
991
992 /* error or timed out */
993 if(!msg)
994 return -1;
995
996 result = CURLE_OK;
997
998 switch(ldap_msgtype(msg)) {
999 case LDAP_RES_SEARCH_RESULT:
1000 lr->msgid = 0;
1001 rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0);
1002 if(rc) {
1003 failf(data, "LDAP local: search ldap_parse_result %s",
1004 ldap_err2string(rc));
1005 result = CURLE_LDAP_SEARCH_FAILED;
1006 break;
1007 }
1008
1009 /* store the latest code for later retrieval */
1010 data->info.httpcode = code;
1011
1012 switch(code) {
1013 case LDAP_SIZELIMIT_EXCEEDED:
1014 infof(data, "There are more than %d entries", lr->nument);
1015 FALLTHROUGH();
1016 case LDAP_SUCCESS:
1017 data->req.size = data->req.bytecount;
1018 break;
1019 default:
1020 failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code),
1021 info ? info : "");
1022 result = CURLE_LDAP_SEARCH_FAILED;
1023 break;
1024 }
1025 if(info)
1026 ldap_memfree(info);
1027 break;
1028 case LDAP_RES_SEARCH_ENTRY:
1029 lr->nument++;
1030 rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
1031 if(rc < 0) {
1032 result = CURLE_RECV_ERROR;
1033 break;
1034 }
1035
1036 result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len,
1037 STRCONST("\n"));
1038 if(result)
1039 break;
1040
1041 for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
1042 rc == LDAP_SUCCESS;
1043 rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
1044 int i;
1045
1046 if(!bv.bv_val)
1047 break;
1048
1049 if(!bvals) {
1050 result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1051 STRCONST(":\n"));
1052 if(result)
1053 break;
1054 continue;
1055 }
1056
1057 binary = bv.bv_len > 7 &&
1058 !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7);
1059
1060 for(i = 0; bvals[i].bv_val != NULL; i++) {
1061 bool binval = FALSE;
1062
1063 result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1064 STRCONST(":"));
1065 if(result)
1066 break;
1067
1068 if(!binary) {
1069 /* check for leading or trailing whitespace */
1070 if(ISBLANK(bvals[i].bv_val[0]) ||
1071 ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1]))
1072 binval = TRUE;
1073 else {
1074 /* check for unprintable characters */
1075 unsigned int j;
1076 for(j = 0; j < bvals[i].bv_len; j++)
1077 if(!ISPRINT(bvals[i].bv_val[j])) {
1078 binval = TRUE;
1079 break;
1080 }
1081 }
1082 }
1083 if(binary || binval) {
1084 char *val_b64 = NULL;
1085 size_t val_b64_sz = 0;
1086
1087 /* Binary value, encode to base64. */
1088 if(bvals[i].bv_len)
1089 result = Curl_base64_encode(bvals[i].bv_val, bvals[i].bv_len,
1090 &val_b64, &val_b64_sz);
1091 if(!result)
1092 result = client_write(data, STRCONST(": "), val_b64, val_b64_sz,
1093 STRCONST("\n"));
1094 free(val_b64);
1095 }
1096 else
1097 result = client_write(data, STRCONST(" "),
1098 bvals[i].bv_val, bvals[i].bv_len,
1099 STRCONST("\n"));
1100 if(result)
1101 break;
1102 }
1103
1104 ber_memfree(bvals);
1105 bvals = NULL;
1106 if(!result)
1107 result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1108 if(result)
1109 break;
1110 }
1111
1112 ber_free(ber, 0);
1113
1114 if(!result)
1115 result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1116 if(!result)
1117 result = CURLE_AGAIN;
1118 break;
1119 }
1120
1121 ldap_msgfree(msg);
1122 *err = result;
1123 return result ? -1 : 0;
1124 }
1125
1126 #ifdef USE_SSL
1127 static int
ldapsb_tls_setup(Sockbuf_IO_Desc * sbiod,void * arg)1128 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
1129 {
1130 sbiod->sbiod_pvt = arg;
1131 return 0;
1132 }
1133
1134 static int
ldapsb_tls_remove(Sockbuf_IO_Desc * sbiod)1135 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
1136 {
1137 sbiod->sbiod_pvt = NULL;
1138 return 0;
1139 }
1140
1141 /* We do not need to do anything because libcurl does it already */
1142 static int
ldapsb_tls_close(Sockbuf_IO_Desc * sbiod)1143 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
1144 {
1145 (void)sbiod;
1146 return 0;
1147 }
1148
1149 static int
ldapsb_tls_ctrl(Sockbuf_IO_Desc * sbiod,int opt,void * arg)1150 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
1151 {
1152 (void)arg;
1153 if(opt == LBER_SB_OPT_DATA_READY) {
1154 struct Curl_easy *data = sbiod->sbiod_pvt;
1155 return Curl_conn_data_pending(data, FIRSTSOCKET);
1156 }
1157 return 0;
1158 }
1159
1160 static ber_slen_t
ldapsb_tls_read(Sockbuf_IO_Desc * sbiod,void * buf,ber_len_t len)1161 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
1162 {
1163 struct Curl_easy *data = sbiod->sbiod_pvt;
1164 ber_slen_t ret = 0;
1165 if(data) {
1166 struct connectdata *conn = data->conn;
1167 if(conn) {
1168 struct ldapconninfo *li = conn->proto.ldapc;
1169 CURLcode err = CURLE_RECV_ERROR;
1170
1171 ret = (li->recv)(data, FIRSTSOCKET, buf, len, &err);
1172 if(ret < 0 && err == CURLE_AGAIN) {
1173 SET_SOCKERRNO(EWOULDBLOCK);
1174 }
1175 }
1176 }
1177 return ret;
1178 }
1179
1180 static ber_slen_t
ldapsb_tls_write(Sockbuf_IO_Desc * sbiod,void * buf,ber_len_t len)1181 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
1182 {
1183 struct Curl_easy *data = sbiod->sbiod_pvt;
1184 ber_slen_t ret = 0;
1185 if(data) {
1186 struct connectdata *conn = data->conn;
1187 if(conn) {
1188 struct ldapconninfo *li = conn->proto.ldapc;
1189 CURLcode err = CURLE_SEND_ERROR;
1190 ret = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &err);
1191 if(ret < 0 && err == CURLE_AGAIN) {
1192 SET_SOCKERRNO(EWOULDBLOCK);
1193 }
1194 }
1195 }
1196 return ret;
1197 }
1198
1199 static Sockbuf_IO ldapsb_tls =
1200 {
1201 ldapsb_tls_setup,
1202 ldapsb_tls_remove,
1203 ldapsb_tls_ctrl,
1204 ldapsb_tls_read,
1205 ldapsb_tls_write,
1206 ldapsb_tls_close
1207 };
1208 #endif /* USE_SSL */
1209
1210 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */
1211