xref: /curl/lib/openldap.c (revision c294f9cb)
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: CURLE_URL_MALFORMAT;
280     rc -= LDAP_URL_SUCCESS;
281     if((size_t) rc < sizeof(url_errs) / sizeof(url_errs[0]))
282       msg = url_errs[rc];
283     failf(data, "LDAP local: %s", msg);
284   }
285   return result;
286 }
287 
288 /* Parse the login options. */
oldap_parse_login_options(struct connectdata * conn)289 static CURLcode oldap_parse_login_options(struct connectdata *conn)
290 {
291   CURLcode result = CURLE_OK;
292   struct ldapconninfo *li = conn->proto.ldapc;
293   const char *ptr = conn->options;
294 
295   while(!result && ptr && *ptr) {
296     const char *key = ptr;
297     const char *value;
298 
299     while(*ptr && *ptr != '=')
300       ptr++;
301 
302     value = ptr + 1;
303 
304     while(*ptr && *ptr != ';')
305       ptr++;
306 
307     if(checkprefix("AUTH=", key))
308       result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value);
309     else
310       result = CURLE_SETOPT_OPTION_SYNTAX;
311 
312     if(*ptr == ';')
313       ptr++;
314   }
315 
316   return result == CURLE_URL_MALFORMAT? CURLE_SETOPT_OPTION_SYNTAX: result;
317 }
318 
oldap_setup_connection(struct Curl_easy * data,struct connectdata * conn)319 static CURLcode oldap_setup_connection(struct Curl_easy *data,
320                                        struct connectdata *conn)
321 {
322   CURLcode result;
323   LDAPURLDesc *lud;
324   (void)conn;
325 
326   /* Early URL syntax check. */
327   result = oldap_url_parse(data, &lud);
328   ldap_free_urldesc(lud);
329 
330   return result;
331 }
332 
333 /*
334  * Get the SASL authentication challenge from the server credential buffer.
335  */
oldap_get_message(struct Curl_easy * data,struct bufref * out)336 static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out)
337 {
338   struct berval *servercred = data->conn->proto.ldapc->servercred;
339 
340   if(!servercred || !servercred->bv_val)
341     return CURLE_WEIRD_SERVER_REPLY;
342   Curl_bufref_set(out, servercred->bv_val, servercred->bv_len, NULL);
343   return CURLE_OK;
344 }
345 
346 /*
347  * Sends an initial SASL bind request to the server.
348  */
oldap_perform_auth(struct Curl_easy * data,const char * mech,const struct bufref * initresp)349 static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
350                                    const struct bufref *initresp)
351 {
352   struct connectdata *conn = data->conn;
353   struct ldapconninfo *li = conn->proto.ldapc;
354   CURLcode result = CURLE_OK;
355   struct berval cred;
356   struct berval *pcred = &cred;
357   int rc;
358 
359   cred.bv_val = (char *) Curl_bufref_ptr(initresp);
360   cred.bv_len = Curl_bufref_len(initresp);
361   if(!cred.bv_val)
362     pcred = NULL;
363   rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid);
364   if(rc != LDAP_SUCCESS)
365     result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
366   return result;
367 }
368 
369 /*
370  * Sends SASL continuation.
371  */
oldap_continue_auth(struct Curl_easy * data,const char * mech,const struct bufref * resp)372 static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
373                                     const struct bufref *resp)
374 {
375   struct connectdata *conn = data->conn;
376   struct ldapconninfo *li = conn->proto.ldapc;
377   CURLcode result = CURLE_OK;
378   struct berval cred;
379   struct berval *pcred = &cred;
380   int rc;
381 
382   cred.bv_val = (char *) Curl_bufref_ptr(resp);
383   cred.bv_len = Curl_bufref_len(resp);
384   if(!cred.bv_val)
385     pcred = NULL;
386   rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid);
387   if(rc != LDAP_SUCCESS)
388     result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
389   return result;
390 }
391 
392 /*
393  * Sends SASL bind cancellation.
394  */
oldap_cancel_auth(struct Curl_easy * data,const char * mech)395 static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech)
396 {
397   struct ldapconninfo *li = data->conn->proto.ldapc;
398   CURLcode result = CURLE_OK;
399   int rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL,
400                           &li->msgid);
401 
402   (void)mech;
403   if(rc != LDAP_SUCCESS)
404     result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
405   return result;
406 }
407 
408 /* Starts LDAP simple bind. */
oldap_perform_bind(struct Curl_easy * data,ldapstate newstate)409 static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate)
410 {
411   CURLcode result = CURLE_OK;
412   struct connectdata *conn = data->conn;
413   struct ldapconninfo *li = conn->proto.ldapc;
414   char *binddn = NULL;
415   struct berval passwd;
416   int rc;
417 
418   passwd.bv_val = NULL;
419   passwd.bv_len = 0;
420 
421   if(data->state.aptr.user) {
422     binddn = conn->user;
423     passwd.bv_val = conn->passwd;
424     passwd.bv_len = strlen(passwd.bv_val);
425   }
426 
427   rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
428                       NULL, NULL, &li->msgid);
429   if(rc == LDAP_SUCCESS)
430     oldap_state(data, newstate);
431   else
432     result = oldap_map_error(rc,
433                              data->state.aptr.user?
434                              CURLE_LOGIN_DENIED: CURLE_LDAP_CANNOT_BIND);
435   return result;
436 }
437 
438 /* Query the supported SASL authentication mechanisms. */
oldap_perform_mechs(struct Curl_easy * data)439 static CURLcode oldap_perform_mechs(struct Curl_easy *data)
440 {
441   CURLcode result = CURLE_OK;
442   struct ldapconninfo *li = data->conn->proto.ldapc;
443   int rc;
444   static const char * const supportedSASLMechanisms[] = {
445     "supportedSASLMechanisms",
446     NULL
447   };
448 
449   rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
450                        (char **) supportedSASLMechanisms, 0,
451                        NULL, NULL, NULL, 0, &li->msgid);
452   if(rc == LDAP_SUCCESS)
453     oldap_state(data, OLDAP_MECHS);
454   else
455     result = oldap_map_error(rc, CURLE_LOGIN_DENIED);
456   return result;
457 }
458 
459 /* Starts SASL bind. */
oldap_perform_sasl(struct Curl_easy * data)460 static CURLcode oldap_perform_sasl(struct Curl_easy *data)
461 {
462   saslprogress progress = SASL_IDLE;
463   struct ldapconninfo *li = data->conn->proto.ldapc;
464   CURLcode result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
465 
466   oldap_state(data, OLDAP_SASL);
467   if(!result && progress != SASL_INPROGRESS)
468     result = CURLE_LOGIN_DENIED;
469   return result;
470 }
471 
472 #ifdef USE_SSL
473 static Sockbuf_IO ldapsb_tls;
474 
ssl_installed(struct connectdata * conn)475 static bool ssl_installed(struct connectdata *conn)
476 {
477   return conn->proto.ldapc->recv != NULL;
478 }
479 
oldap_ssl_connect(struct Curl_easy * data,ldapstate newstate)480 static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
481 {
482   CURLcode result = CURLE_OK;
483   struct connectdata *conn = data->conn;
484   struct ldapconninfo *li = conn->proto.ldapc;
485   bool ssldone = 0;
486 
487   result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
488   if(!result) {
489     oldap_state(data, newstate);
490 
491     if(ssldone) {
492       Sockbuf *sb;
493 
494       /* Install the libcurl SSL handlers into the sockbuf. */
495       ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
496       ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
497       li->recv = conn->recv[FIRSTSOCKET];
498       li->send = conn->send[FIRSTSOCKET];
499     }
500   }
501 
502   return result;
503 }
504 
505 /* Send the STARTTLS request */
oldap_perform_starttls(struct Curl_easy * data)506 static CURLcode oldap_perform_starttls(struct Curl_easy *data)
507 {
508   CURLcode result = CURLE_OK;
509   struct ldapconninfo *li = data->conn->proto.ldapc;
510   int rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
511 
512   if(rc == LDAP_SUCCESS)
513     oldap_state(data, OLDAP_STARTTLS);
514   else
515     result = oldap_map_error(rc, CURLE_USE_SSL_FAILED);
516   return result;
517 }
518 #endif
519 
oldap_connect(struct Curl_easy * data,bool * done)520 static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
521 {
522   struct connectdata *conn = data->conn;
523   struct ldapconninfo *li;
524   static const int version = LDAP_VERSION3;
525   int rc;
526   char *hosturl;
527 #ifdef CURL_OPENLDAP_DEBUG
528   static int do_trace = -1;
529 #endif
530 
531   (void)done;
532 
533   DEBUGASSERT(!conn->proto.ldapc);
534   li = calloc(1, sizeof(struct ldapconninfo));
535   if(!li)
536     return CURLE_OUT_OF_MEMORY;
537   else {
538     CURLcode result;
539     li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
540     conn->proto.ldapc = li;
541 
542     /* Initialize the SASL storage */
543     Curl_sasl_init(&li->sasl, data, &saslldap);
544 
545     /* Clear the TLS upgraded flag */
546     conn->bits.tls_upgraded = FALSE;
547 
548     result = oldap_parse_login_options(conn);
549     if(result)
550       return result;
551   }
552 
553   hosturl = aprintf("%s://%s%s%s:%d",
554                     conn->handler->scheme,
555                     conn->bits.ipv6_ip? "[": "",
556                     conn->host.name,
557                     conn->bits.ipv6_ip? "]": "",
558                     conn->remote_port);
559   if(!hosturl)
560     return CURLE_OUT_OF_MEMORY;
561 
562   rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
563   if(rc) {
564     failf(data, "LDAP local: Cannot connect to %s, %s",
565           hosturl, ldap_err2string(rc));
566     free(hosturl);
567     return CURLE_COULDNT_CONNECT;
568   }
569 
570   free(hosturl);
571 
572 #ifdef CURL_OPENLDAP_DEBUG
573   if(do_trace < 0) {
574     const char *env = getenv("CURL_OPENLDAP_TRACE");
575     do_trace = (env && strtol(env, NULL, 10) > 0);
576   }
577   if(do_trace)
578     ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
579 #endif
580 
581   /* Try version 3 first. */
582   ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
583 
584   /* Do not chase referrals. */
585   ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
586 
587 #ifdef USE_SSL
588   if(conn->handler->flags & PROTOPT_SSL)
589     return oldap_ssl_connect(data, OLDAP_SSL);
590 
591   if(data->set.use_ssl) {
592     CURLcode result = oldap_perform_starttls(data);
593 
594     if(!result || data->set.use_ssl != CURLUSESSL_TRY)
595       return result;
596   }
597 #endif
598 
599   if(li->sasl.prefmech != SASL_AUTH_NONE)
600     return oldap_perform_mechs(data);
601 
602   /* Force bind even if anonymous bind is not needed in protocol version 3
603      to detect missing version 3 support. */
604   return oldap_perform_bind(data, OLDAP_BIND);
605 }
606 
607 /* Handle the supported SASL mechanisms query response */
oldap_state_mechs_resp(struct Curl_easy * data,LDAPMessage * msg,int code)608 static CURLcode oldap_state_mechs_resp(struct Curl_easy *data,
609                                        LDAPMessage *msg, int code)
610 {
611   struct connectdata *conn = data->conn;
612   struct ldapconninfo *li = conn->proto.ldapc;
613   int rc;
614   BerElement *ber = NULL;
615   CURLcode result = CURLE_OK;
616   struct berval bv, *bvals;
617 
618   switch(ldap_msgtype(msg)) {
619   case LDAP_RES_SEARCH_ENTRY:
620     /* Got a list of supported SASL mechanisms. */
621     if(code != LDAP_SUCCESS && code != LDAP_NO_RESULTS_RETURNED)
622       return CURLE_LOGIN_DENIED;
623 
624     rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
625     if(rc < 0)
626       return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING);
627     for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
628         rc == LDAP_SUCCESS;
629         rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
630       int i;
631 
632       if(!bv.bv_val)
633         break;
634 
635       if(bvals) {
636         for(i = 0; bvals[i].bv_val; i++) {
637           size_t llen;
638           unsigned short mech = Curl_sasl_decode_mech((char *) bvals[i].bv_val,
639                                                       bvals[i].bv_len, &llen);
640           if(bvals[i].bv_len == llen)
641             li->sasl.authmechs |= mech;
642         }
643         ber_memfree(bvals);
644       }
645     }
646     ber_free(ber, 0);
647     break;
648 
649   case LDAP_RES_SEARCH_RESULT:
650     switch(code) {
651     case LDAP_SIZELIMIT_EXCEEDED:
652       infof(data, "Too many authentication mechanisms\n");
653       FALLTHROUGH();
654     case LDAP_SUCCESS:
655     case LDAP_NO_RESULTS_RETURNED:
656       if(Curl_sasl_can_authenticate(&li->sasl, data))
657         result = oldap_perform_sasl(data);
658       else
659         result = CURLE_LOGIN_DENIED;
660       break;
661     default:
662       result = oldap_map_error(code, CURLE_LOGIN_DENIED);
663       break;
664     }
665     break;
666   default:
667     break;
668   }
669   return result;
670 }
671 
672 /* Handle a SASL bind response. */
oldap_state_sasl_resp(struct Curl_easy * data,LDAPMessage * msg,int code)673 static CURLcode oldap_state_sasl_resp(struct Curl_easy *data,
674                                       LDAPMessage *msg, int code)
675 {
676   struct connectdata *conn = data->conn;
677   struct ldapconninfo *li = conn->proto.ldapc;
678   CURLcode result = CURLE_OK;
679   saslprogress progress;
680   int rc;
681 
682   li->servercred = NULL;
683   rc = ldap_parse_sasl_bind_result(li->ld, msg, &li->servercred, 0);
684   if(rc != LDAP_SUCCESS) {
685     failf(data, "LDAP local: sasl ldap_parse_result %s", ldap_err2string(rc));
686     result = oldap_map_error(rc, CURLE_LOGIN_DENIED);
687   }
688   else {
689     result = Curl_sasl_continue(&li->sasl, data, code, &progress);
690     if(!result && progress != SASL_INPROGRESS)
691       oldap_state(data, OLDAP_STOP);
692   }
693 
694   if(li->servercred)
695     ber_bvfree(li->servercred);
696   return result;
697 }
698 
699 /* Handle a simple bind response. */
oldap_state_bind_resp(struct Curl_easy * data,LDAPMessage * msg,int code)700 static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg,
701                                       int code)
702 {
703   struct connectdata *conn = data->conn;
704   struct ldapconninfo *li = conn->proto.ldapc;
705   CURLcode result = CURLE_OK;
706   struct berval *bv = NULL;
707   int rc;
708 
709   if(code != LDAP_SUCCESS)
710     return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND);
711 
712   rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0);
713   if(rc != LDAP_SUCCESS) {
714     failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s",
715           ldap_err2string(rc));
716     result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
717   }
718   else
719     oldap_state(data, OLDAP_STOP);
720 
721   if(bv)
722     ber_bvfree(bv);
723   return result;
724 }
725 
oldap_connecting(struct Curl_easy * data,bool * done)726 static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
727 {
728   CURLcode result = CURLE_OK;
729   struct connectdata *conn = data->conn;
730   struct ldapconninfo *li = conn->proto.ldapc;
731   LDAPMessage *msg = NULL;
732   struct timeval tv = {0, 0};
733   int code = LDAP_SUCCESS;
734   int rc;
735 
736   if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) {
737     /* Get response to last command. */
738     rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg);
739     switch(rc) {
740     case 0:                               /* Timed out. */
741       return CURLE_OK;
742     case LDAP_RES_SEARCH_ENTRY:
743     case LDAP_RES_SEARCH_REFERENCE:
744       break;
745     default:
746       li->msgid = 0;                      /* Nothing to abandon upon error. */
747       if(rc < 0) {
748         failf(data, "LDAP local: connecting ldap_result %s",
749               ldap_err2string(rc));
750         return oldap_map_error(rc, CURLE_COULDNT_CONNECT);
751       }
752       break;
753     }
754 
755     /* Get error code from message. */
756     rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0);
757     if(rc)
758       code = rc;
759     else {
760       /* store the latest code for later retrieval */
761       data->info.httpcode = code;
762     }
763 
764     /* If protocol version 3 is not supported, fallback to version 2. */
765     if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 &&
766 #ifdef USE_SSL
767        (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) &&
768 #endif
769        li->sasl.prefmech == SASL_AUTH_NONE) {
770       static const int version = LDAP_VERSION2;
771 
772       ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
773       ldap_msgfree(msg);
774       return oldap_perform_bind(data, OLDAP_BINDV2);
775     }
776   }
777 
778   /* Handle response message according to current state. */
779   switch(li->state) {
780 
781 #ifdef USE_SSL
782   case OLDAP_SSL:
783     result = oldap_ssl_connect(data, OLDAP_SSL);
784     if(!result && ssl_installed(conn)) {
785       if(li->sasl.prefmech != SASL_AUTH_NONE)
786         result = oldap_perform_mechs(data);
787       else
788         result = oldap_perform_bind(data, OLDAP_BIND);
789     }
790     break;
791   case OLDAP_STARTTLS:
792     if(code != LDAP_SUCCESS) {
793       if(data->set.use_ssl != CURLUSESSL_TRY)
794         result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
795       else if(li->sasl.prefmech != SASL_AUTH_NONE)
796         result = oldap_perform_mechs(data);
797       else
798         result = oldap_perform_bind(data, OLDAP_BIND);
799       break;
800     }
801     result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
802     if(result)
803       break;
804     FALLTHROUGH();
805   case OLDAP_TLS:
806     result = oldap_ssl_connect(data, OLDAP_TLS);
807     if(result)
808       result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
809     else if(ssl_installed(conn)) {
810       conn->bits.tls_upgraded = TRUE;
811       if(li->sasl.prefmech != SASL_AUTH_NONE)
812         result = oldap_perform_mechs(data);
813       else if(data->state.aptr.user)
814         result = oldap_perform_bind(data, OLDAP_BIND);
815       else {
816         /* Version 3 supported: no bind required */
817         oldap_state(data, OLDAP_STOP);
818         result = CURLE_OK;
819       }
820     }
821     break;
822 #endif
823 
824   case OLDAP_MECHS:
825     result = oldap_state_mechs_resp(data, msg, code);
826     break;
827   case OLDAP_SASL:
828     result = oldap_state_sasl_resp(data, msg, code);
829     break;
830   case OLDAP_BIND:
831   case OLDAP_BINDV2:
832     result = oldap_state_bind_resp(data, msg, code);
833     break;
834   default:
835     /* internal error */
836     result = CURLE_COULDNT_CONNECT;
837     break;
838   }
839 
840   ldap_msgfree(msg);
841 
842   *done = li->state == OLDAP_STOP;
843   if(*done)
844     conn->recv[FIRSTSOCKET] = oldap_recv;
845 
846   if(result && li->msgid) {
847     ldap_abandon_ext(li->ld, li->msgid, NULL, NULL);
848     li->msgid = 0;
849   }
850   return result;
851 }
852 
oldap_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead_connection)853 static CURLcode oldap_disconnect(struct Curl_easy *data,
854                                  struct connectdata *conn,
855                                  bool dead_connection)
856 {
857   struct ldapconninfo *li = conn->proto.ldapc;
858   (void) dead_connection;
859 #ifndef USE_SSL
860   (void)data;
861 #endif
862 
863   if(li) {
864     if(li->ld) {
865 #ifdef USE_SSL
866       if(ssl_installed(conn)) {
867         Sockbuf *sb;
868         ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
869         ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
870       }
871 #endif
872       ldap_unbind_ext(li->ld, NULL, NULL);
873       li->ld = NULL;
874     }
875     Curl_sasl_cleanup(conn, li->sasl.authused);
876     conn->proto.ldapc = NULL;
877     free(li);
878   }
879   return CURLE_OK;
880 }
881 
oldap_do(struct Curl_easy * data,bool * done)882 static CURLcode oldap_do(struct Curl_easy *data, bool *done)
883 {
884   struct connectdata *conn = data->conn;
885   struct ldapconninfo *li = conn->proto.ldapc;
886   struct ldapreqinfo *lr;
887   CURLcode result;
888   int rc;
889   LDAPURLDesc *lud;
890   int msgid;
891 
892   connkeep(conn, "OpenLDAP do");
893 
894   infof(data, "LDAP local: %s", data->state.url);
895 
896   result = oldap_url_parse(data, &lud);
897   if(!result) {
898 #ifdef USE_SSL
899     if(ssl_installed(conn)) {
900       Sockbuf *sb;
901       /* re-install the libcurl SSL handlers into the sockbuf. */
902       ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
903       ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
904     }
905 #endif
906 
907     rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
908                          lud->lud_filter, lud->lud_attrs, 0,
909                          NULL, NULL, NULL, 0, &msgid);
910     ldap_free_urldesc(lud);
911     if(rc != LDAP_SUCCESS) {
912       failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
913       result = CURLE_LDAP_SEARCH_FAILED;
914     }
915     else {
916       lr = calloc(1, sizeof(struct ldapreqinfo));
917       if(!lr) {
918         ldap_abandon_ext(li->ld, msgid, NULL, NULL);
919         result = CURLE_OUT_OF_MEMORY;
920       }
921       else {
922         lr->msgid = msgid;
923         data->req.p.ldap = lr;
924         Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
925         *done = TRUE;
926       }
927     }
928   }
929   return result;
930 }
931 
oldap_done(struct Curl_easy * data,CURLcode res,bool premature)932 static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
933                            bool premature)
934 {
935   struct connectdata *conn = data->conn;
936   struct ldapreqinfo *lr = data->req.p.ldap;
937 
938   (void)res;
939   (void)premature;
940 
941   if(lr) {
942     /* if there was a search in progress, abandon it */
943     if(lr->msgid) {
944       struct ldapconninfo *li = conn->proto.ldapc;
945       ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
946       lr->msgid = 0;
947     }
948     data->req.p.ldap = NULL;
949     free(lr);
950   }
951 
952   return CURLE_OK;
953 }
954 
client_write(struct Curl_easy * data,const char * prefix,size_t plen,const char * value,size_t len,const char * suffix,size_t slen)955 static CURLcode client_write(struct Curl_easy *data,
956                              const char *prefix, size_t plen,
957                              const char *value, size_t len,
958                              const char *suffix, size_t slen)
959 {
960   CURLcode result = CURLE_OK;
961 
962   if(prefix) {
963     /* If we have a zero-length value and the prefix ends with a space
964        separator, drop the latter. */
965     if(!len && plen && prefix[plen - 1] == ' ')
966       plen--;
967     result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen);
968   }
969   if(!result && value) {
970     result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len);
971   }
972   if(!result && suffix) {
973     result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen);
974   }
975   return result;
976 }
977 
oldap_recv(struct Curl_easy * data,int sockindex,char * buf,size_t len,CURLcode * err)978 static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
979                           size_t len, CURLcode *err)
980 {
981   struct connectdata *conn = data->conn;
982   struct ldapconninfo *li = conn->proto.ldapc;
983   struct ldapreqinfo *lr = data->req.p.ldap;
984   int rc;
985   LDAPMessage *msg = NULL;
986   BerElement *ber = NULL;
987   struct timeval tv = {0, 0};
988   struct berval bv, *bvals;
989   int binary = 0;
990   CURLcode result = CURLE_AGAIN;
991   int code;
992   char *info = NULL;
993 
994   (void)len;
995   (void)buf;
996   (void)sockindex;
997 
998   rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
999   if(rc < 0) {
1000     failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
1001     result = CURLE_RECV_ERROR;
1002   }
1003 
1004   *err = result;
1005 
1006   /* error or timed out */
1007   if(!msg)
1008     return -1;
1009 
1010   result = CURLE_OK;
1011 
1012   switch(ldap_msgtype(msg)) {
1013   case LDAP_RES_SEARCH_RESULT:
1014     lr->msgid = 0;
1015     rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0);
1016     if(rc) {
1017       failf(data, "LDAP local: search ldap_parse_result %s",
1018             ldap_err2string(rc));
1019       result = CURLE_LDAP_SEARCH_FAILED;
1020       break;
1021     }
1022 
1023     /* store the latest code for later retrieval */
1024     data->info.httpcode = code;
1025 
1026     switch(code) {
1027     case LDAP_SIZELIMIT_EXCEEDED:
1028       infof(data, "There are more than %d entries", lr->nument);
1029       FALLTHROUGH();
1030     case LDAP_SUCCESS:
1031       data->req.size = data->req.bytecount;
1032       break;
1033     default:
1034       failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code),
1035             info ? info : "");
1036       result = CURLE_LDAP_SEARCH_FAILED;
1037       break;
1038     }
1039     if(info)
1040       ldap_memfree(info);
1041     break;
1042   case LDAP_RES_SEARCH_ENTRY:
1043     lr->nument++;
1044     rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
1045     if(rc < 0) {
1046       result = CURLE_RECV_ERROR;
1047       break;
1048     }
1049 
1050     result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len,
1051                           STRCONST("\n"));
1052     if(result)
1053       break;
1054 
1055     for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
1056         rc == LDAP_SUCCESS;
1057         rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
1058       int i;
1059 
1060       if(!bv.bv_val)
1061         break;
1062 
1063       if(!bvals) {
1064         result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1065                               STRCONST(":\n"));
1066         if(result)
1067           break;
1068         continue;
1069       }
1070 
1071       binary = bv.bv_len > 7 &&
1072                !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7);
1073 
1074       for(i = 0; bvals[i].bv_val != NULL; i++) {
1075         int binval = 0;
1076 
1077         result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1078                               STRCONST(":"));
1079         if(result)
1080           break;
1081 
1082         if(!binary) {
1083           /* check for leading or trailing whitespace */
1084           if(ISBLANK(bvals[i].bv_val[0]) ||
1085              ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1]))
1086             binval = 1;
1087           else {
1088             /* check for unprintable characters */
1089             unsigned int j;
1090             for(j = 0; j < bvals[i].bv_len; j++)
1091               if(!ISPRINT(bvals[i].bv_val[j])) {
1092                 binval = 1;
1093                 break;
1094               }
1095           }
1096         }
1097         if(binary || binval) {
1098           char *val_b64 = NULL;
1099           size_t val_b64_sz = 0;
1100 
1101           /* Binary value, encode to base64. */
1102           if(bvals[i].bv_len)
1103             result = Curl_base64_encode(bvals[i].bv_val, bvals[i].bv_len,
1104                                         &val_b64, &val_b64_sz);
1105           if(!result)
1106             result = client_write(data, STRCONST(": "), val_b64, val_b64_sz,
1107                                   STRCONST("\n"));
1108           free(val_b64);
1109         }
1110         else
1111           result = client_write(data, STRCONST(" "),
1112                                 bvals[i].bv_val, bvals[i].bv_len,
1113                                 STRCONST("\n"));
1114         if(result)
1115           break;
1116       }
1117 
1118       ber_memfree(bvals);
1119       bvals = NULL;
1120       if(!result)
1121         result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1122       if(result)
1123         break;
1124     }
1125 
1126     ber_free(ber, 0);
1127 
1128     if(!result)
1129       result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1130     if(!result)
1131       result = CURLE_AGAIN;
1132     break;
1133   }
1134 
1135   ldap_msgfree(msg);
1136   *err = result;
1137   return result? -1: 0;
1138 }
1139 
1140 #ifdef USE_SSL
1141 static int
ldapsb_tls_setup(Sockbuf_IO_Desc * sbiod,void * arg)1142 ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
1143 {
1144   sbiod->sbiod_pvt = arg;
1145   return 0;
1146 }
1147 
1148 static int
ldapsb_tls_remove(Sockbuf_IO_Desc * sbiod)1149 ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
1150 {
1151   sbiod->sbiod_pvt = NULL;
1152   return 0;
1153 }
1154 
1155 /* We don't need to do anything because libcurl does it already */
1156 static int
ldapsb_tls_close(Sockbuf_IO_Desc * sbiod)1157 ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
1158 {
1159   (void)sbiod;
1160   return 0;
1161 }
1162 
1163 static int
ldapsb_tls_ctrl(Sockbuf_IO_Desc * sbiod,int opt,void * arg)1164 ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
1165 {
1166   (void)arg;
1167   if(opt == LBER_SB_OPT_DATA_READY) {
1168     struct Curl_easy *data = sbiod->sbiod_pvt;
1169     return Curl_conn_data_pending(data, FIRSTSOCKET);
1170   }
1171   return 0;
1172 }
1173 
1174 static ber_slen_t
ldapsb_tls_read(Sockbuf_IO_Desc * sbiod,void * buf,ber_len_t len)1175 ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
1176 {
1177   struct Curl_easy *data = sbiod->sbiod_pvt;
1178   ber_slen_t ret = 0;
1179   if(data) {
1180     struct connectdata *conn = data->conn;
1181     if(conn) {
1182       struct ldapconninfo *li = conn->proto.ldapc;
1183       CURLcode err = CURLE_RECV_ERROR;
1184 
1185       ret = (li->recv)(data, FIRSTSOCKET, buf, len, &err);
1186       if(ret < 0 && err == CURLE_AGAIN) {
1187         SET_SOCKERRNO(EWOULDBLOCK);
1188       }
1189     }
1190   }
1191   return ret;
1192 }
1193 
1194 static ber_slen_t
ldapsb_tls_write(Sockbuf_IO_Desc * sbiod,void * buf,ber_len_t len)1195 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
1196 {
1197   struct Curl_easy *data = sbiod->sbiod_pvt;
1198   ber_slen_t ret = 0;
1199   if(data) {
1200     struct connectdata *conn = data->conn;
1201     if(conn) {
1202       struct ldapconninfo *li = conn->proto.ldapc;
1203       CURLcode err = CURLE_SEND_ERROR;
1204       ret = (li->send)(data, FIRSTSOCKET, buf, len, &err);
1205       if(ret < 0 && err == CURLE_AGAIN) {
1206         SET_SOCKERRNO(EWOULDBLOCK);
1207       }
1208     }
1209   }
1210   return ret;
1211 }
1212 
1213 static Sockbuf_IO ldapsb_tls =
1214 {
1215   ldapsb_tls_setup,
1216   ldapsb_tls_remove,
1217   ldapsb_tls_ctrl,
1218   ldapsb_tls_read,
1219   ldapsb_tls_write,
1220   ldapsb_tls_close
1221 };
1222 #endif /* USE_SSL */
1223 
1224 #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */
1225