VirtualBox

source: vbox/trunk/src/libs/curl-7.83.1/lib/openldap.c@ 97623

Last change on this file since 97623 was 95312, checked in by vboxsync, 3 years ago

libs/{curl,libxml2}: OSE export fixes, bugref:8515

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette