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