• 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  * state()
203  *
204  * This is the ONLY way to change LDAP state!
205  */
state(struct Curl_easy * data,ldapstate newstate)206 static void 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     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     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   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     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     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       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     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         state(data, OLDAP_STOP); /* Version 3 supported: no bind required */
808         result = CURLE_OK;
809       }
810     }
811     break;
812 #endif
813 
814   case OLDAP_MECHS:
815     result = oldap_state_mechs_resp(data, msg, code);
816     break;
817   case OLDAP_SASL:
818     result = oldap_state_sasl_resp(data, msg, code);
819     break;
820   case OLDAP_BIND:
821   case OLDAP_BINDV2:
822     result = oldap_state_bind_resp(data, msg, code);
823     break;
824   default:
825     /* internal error */
826     result = CURLE_COULDNT_CONNECT;
827     break;
828   }
829 
830   ldap_msgfree(msg);
831 
832   *done = li->state == OLDAP_STOP;
833   if(*done)
834     conn->recv[FIRSTSOCKET] = oldap_recv;
835 
836   if(result && li->msgid) {
837     ldap_abandon_ext(li->ld, li->msgid, NULL, NULL);
838     li->msgid = 0;
839   }
840   return result;
841 }
842 
oldap_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead_connection)843 static CURLcode oldap_disconnect(struct Curl_easy *data,
844                                  struct connectdata *conn,
845                                  bool dead_connection)
846 {
847   struct ldapconninfo *li = conn->proto.ldapc;
848   (void) dead_connection;
849 #ifndef USE_SSL
850   (void)data;
851 #endif
852 
853   if(li) {
854     if(li->ld) {
855 #ifdef USE_SSL
856       if(ssl_installed(conn)) {
857         Sockbuf *sb;
858         ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
859         ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
860       }
861 #endif
862       ldap_unbind_ext(li->ld, NULL, NULL);
863       li->ld = NULL;
864     }
865     Curl_sasl_cleanup(conn, li->sasl.authused);
866     conn->proto.ldapc = NULL;
867     free(li);
868   }
869   return CURLE_OK;
870 }
871 
oldap_do(struct Curl_easy * data,bool * done)872 static CURLcode oldap_do(struct Curl_easy *data, bool *done)
873 {
874   struct connectdata *conn = data->conn;
875   struct ldapconninfo *li = conn->proto.ldapc;
876   struct ldapreqinfo *lr;
877   CURLcode result;
878   int rc;
879   LDAPURLDesc *lud;
880   int msgid;
881 
882   connkeep(conn, "OpenLDAP do");
883 
884   infof(data, "LDAP local: %s", data->state.url);
885 
886   result = oldap_url_parse(data, &lud);
887   if(!result) {
888     rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
889                          lud->lud_filter, lud->lud_attrs, 0,
890                          NULL, NULL, NULL, 0, &msgid);
891     ldap_free_urldesc(lud);
892     if(rc != LDAP_SUCCESS) {
893       failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
894       result = CURLE_LDAP_SEARCH_FAILED;
895     }
896     else {
897       lr = calloc(1, sizeof(struct ldapreqinfo));
898       if(!lr) {
899         ldap_abandon_ext(li->ld, msgid, NULL, NULL);
900         result = CURLE_OUT_OF_MEMORY;
901       }
902       else {
903         lr->msgid = msgid;
904         data->req.p.ldap = lr;
905         Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
906         *done = TRUE;
907       }
908     }
909   }
910   return result;
911 }
912 
oldap_done(struct Curl_easy * data,CURLcode res,bool premature)913 static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
914                            bool premature)
915 {
916   struct connectdata *conn = data->conn;
917   struct ldapreqinfo *lr = data->req.p.ldap;
918 
919   (void)res;
920   (void)premature;
921 
922   if(lr) {
923     /* if there was a search in progress, abandon it */
924     if(lr->msgid) {
925       struct ldapconninfo *li = conn->proto.ldapc;
926       ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
927       lr->msgid = 0;
928     }
929     data->req.p.ldap = NULL;
930     free(lr);
931   }
932 
933   return CURLE_OK;
934 }
935 
client_write(struct Curl_easy * data,const char * prefix,size_t plen,const char * value,size_t len,const char * suffix,size_t slen)936 static CURLcode client_write(struct Curl_easy *data,
937                              const char *prefix, size_t plen,
938                              const char *value, size_t len,
939                              const char *suffix, size_t slen)
940 {
941   CURLcode result = CURLE_OK;
942 
943   if(prefix) {
944     /* If we have a zero-length value and the prefix ends with a space
945        separator, drop the latter. */
946     if(!len && plen && prefix[plen - 1] == ' ')
947       plen--;
948     result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen);
949     if(!result)
950       data->req.bytecount += plen;
951   }
952   if(!result && value) {
953     result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len);
954     if(!result)
955       data->req.bytecount += len;
956   }
957   if(!result && suffix) {
958     result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen);
959     if(!result)
960       data->req.bytecount += slen;
961   }
962   return result;
963 }
964 
oldap_recv(struct Curl_easy * data,int sockindex,char * buf,size_t len,CURLcode * err)965 static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
966                           size_t len, CURLcode *err)
967 {
968   struct connectdata *conn = data->conn;
969   struct ldapconninfo *li = conn->proto.ldapc;
970   struct ldapreqinfo *lr = data->req.p.ldap;
971   int rc;
972   LDAPMessage *msg = NULL;
973   BerElement *ber = NULL;
974   struct timeval tv = {0, 0};
975   struct berval bv, *bvals;
976   int binary = 0;
977   CURLcode result = CURLE_AGAIN;
978   int code;
979   char *info = NULL;
980 
981   (void)len;
982   (void)buf;
983   (void)sockindex;
984 
985   rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
986   if(rc < 0) {
987     failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
988     result = CURLE_RECV_ERROR;
989   }
990 
991   *err = result;
992 
993   /* error or timed out */
994   if(!msg)
995     return -1;
996 
997   result = CURLE_OK;
998 
999   switch(ldap_msgtype(msg)) {
1000   case LDAP_RES_SEARCH_RESULT:
1001     lr->msgid = 0;
1002     rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0);
1003     if(rc) {
1004       failf(data, "LDAP local: search ldap_parse_result %s",
1005             ldap_err2string(rc));
1006       result = CURLE_LDAP_SEARCH_FAILED;
1007       break;
1008     }
1009 
1010     /* store the latest code for later retrieval */
1011     data->info.httpcode = code;
1012 
1013     switch(code) {
1014     case LDAP_SIZELIMIT_EXCEEDED:
1015       infof(data, "There are more than %d entries", lr->nument);
1016       /* FALLTHROUGH */
1017     case LDAP_SUCCESS:
1018       data->req.size = data->req.bytecount;
1019       break;
1020     default:
1021       failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code),
1022             info ? info : "");
1023       result = CURLE_LDAP_SEARCH_FAILED;
1024       break;
1025     }
1026     if(info)
1027       ldap_memfree(info);
1028     break;
1029   case LDAP_RES_SEARCH_ENTRY:
1030     lr->nument++;
1031     rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
1032     if(rc < 0) {
1033       result = CURLE_RECV_ERROR;
1034       break;
1035     }
1036 
1037     result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len,
1038                           STRCONST("\n"));
1039     if(result)
1040       break;
1041 
1042     for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
1043         rc == LDAP_SUCCESS;
1044         rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
1045       int i;
1046 
1047       if(!bv.bv_val)
1048         break;
1049 
1050       if(!bvals) {
1051         result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1052                               STRCONST(":\n"));
1053         if(result)
1054           break;
1055         continue;
1056       }
1057 
1058       binary = bv.bv_len > 7 &&
1059                !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7);
1060 
1061       for(i = 0; bvals[i].bv_val != NULL; i++) {
1062         int binval = 0;
1063 
1064         result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1065                               STRCONST(":"));
1066         if(result)
1067           break;
1068 
1069         if(!binary) {
1070           /* check for leading or trailing whitespace */
1071           if(ISBLANK(bvals[i].bv_val[0]) ||
1072              ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1]))
1073             binval = 1;
1074           else {
1075             /* check for unprintable characters */
1076             unsigned int j;
1077             for(j = 0; j < bvals[i].bv_len; j++)
1078               if(!ISPRINT(bvals[i].bv_val[j])) {
1079                 binval = 1;
1080                 break;
1081               }
1082           }
1083         }
1084         if(binary || binval) {
1085           char *val_b64 = NULL;
1086           size_t val_b64_sz = 0;
1087 
1088           /* Binary value, encode to base64. */
1089           if(bvals[i].bv_len)
1090             result = Curl_base64_encode(bvals[i].bv_val, bvals[i].bv_len,
1091                                         &val_b64, &val_b64_sz);
1092           if(!result)
1093             result = client_write(data, STRCONST(": "), val_b64, val_b64_sz,
1094                                   STRCONST("\n"));
1095           free(val_b64);
1096         }
1097         else
1098           result = client_write(data, STRCONST(" "),
1099                                 bvals[i].bv_val, bvals[i].bv_len,
1100                                 STRCONST("\n"));
1101         if(result)
1102           break;
1103       }
1104 
1105       ber_memfree(bvals);
1106       bvals = NULL;
1107       if(!result)
1108         result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1109       if(result)
1110         break;
1111     }
1112 
1113     ber_free(ber, 0);
1114 
1115     if(!result)
1116       result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1117     if(!result)
1118       result = CURLE_AGAIN;
1119     break;
1120   }
1121 
1122   ldap_msgfree(msg);
1123   *err = result;
1124   return result? -1: 0;
1125 }
1126 
1127 #ifdef USE_SSL
1128 static int
ldapsb_tls_setup(Sockbuf_IO_Desc * sbiod,void * arg)1129 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
1130 {
1131   sbiod->sbiod_pvt = arg;
1132   return 0;
1133 }
1134 
1135 static int
ldapsb_tls_remove(Sockbuf_IO_Desc * sbiod)1136 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
1137 {
1138   sbiod->sbiod_pvt = NULL;
1139   return 0;
1140 }
1141 
1142 /* We don't need to do anything because libcurl does it already */
1143 static int
ldapsb_tls_close(Sockbuf_IO_Desc * sbiod)1144 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
1145 {
1146   (void)sbiod;
1147   return 0;
1148 }
1149 
1150 static int
ldapsb_tls_ctrl(Sockbuf_IO_Desc * sbiod,int opt,void * arg)1151 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
1152 {
1153   (void)arg;
1154   if(opt == LBER_SB_OPT_DATA_READY) {
1155     struct Curl_easy *data = sbiod->sbiod_pvt;
1156     return Curl_conn_data_pending(data, FIRSTSOCKET);
1157   }
1158   return 0;
1159 }
1160 
1161 static ber_slen_t
ldapsb_tls_read(Sockbuf_IO_Desc * sbiod,void * buf,ber_len_t len)1162 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
1163 {
1164   struct Curl_easy *data = sbiod->sbiod_pvt;
1165   ber_slen_t ret = 0;
1166   if(data) {
1167     struct connectdata *conn = data->conn;
1168     if(conn) {
1169       struct ldapconninfo *li = conn->proto.ldapc;
1170       CURLcode err = CURLE_RECV_ERROR;
1171 
1172       ret = (li->recv)(data, FIRSTSOCKET, buf, len, &err);
1173       if(ret < 0 && err == CURLE_AGAIN) {
1174         SET_SOCKERRNO(EWOULDBLOCK);
1175       }
1176     }
1177   }
1178   return ret;
1179 }
1180 
1181 static ber_slen_t
ldapsb_tls_write(Sockbuf_IO_Desc * sbiod,void * buf,ber_len_t len)1182 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
1183 {
1184   struct Curl_easy *data = sbiod->sbiod_pvt;
1185   ber_slen_t ret = 0;
1186   if(data) {
1187     struct connectdata *conn = data->conn;
1188     if(conn) {
1189       struct ldapconninfo *li = conn->proto.ldapc;
1190       CURLcode err = CURLE_SEND_ERROR;
1191       ret = (li->send)(data, FIRSTSOCKET, buf, len, &err);
1192       if(ret < 0 && err == CURLE_AGAIN) {
1193         SET_SOCKERRNO(EWOULDBLOCK);
1194       }
1195     }
1196   }
1197   return ret;
1198 }
1199 
1200 static Sockbuf_IO ldapsb_tls =
1201 {
1202   ldapsb_tls_setup,
1203   ldapsb_tls_remove,
1204   ldapsb_tls_ctrl,
1205   ldapsb_tls_read,
1206   ldapsb_tls_write,
1207   ldapsb_tls_close
1208 };
1209 #endif /* USE_SSL */
1210 
1211 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */
1212