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