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