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