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