Coverage Report

Created: 2026-04-12 06:56

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