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