Coverage Report

Created: 2026-07-16 06:10

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