• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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