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