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