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