Coverage Report

Created: 2026-09-14 07:07

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
2.95k
#define CURL_META_LDAP_EASY   "meta:proto:ldap:easy"
114
/* meta key for storing ldapconninfo at connection */
115
11.6k
#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
2.08k
{
125
2.08k
#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
126
  /* for debug purposes */
127
2.08k
  static const char * const names[] = {
128
2.08k
    "STOP",
129
2.08k
    "SSL",
130
2.08k
    "STARTTLS",
131
2.08k
    "TLS",
132
2.08k
    "MECHS",
133
2.08k
    "SASL",
134
2.08k
    "BIND",
135
2.08k
    "BINDV2",
136
    /* LAST */
137
2.08k
  };
138
139
2.08k
  if(li->state != newstate)
140
2.08k
    infof(data, "LDAP %p state change from %s to %s",
141
2.08k
          (void *)li, names[li->state], names[newstate]);
142
#else
143
  (void)data;
144
#endif
145
2.08k
  li->state = newstate;
146
2.08k
}
147
148
/* Map some particular LDAP error codes to CURLcode values. */
149
static CURLcode oldap_map_error(int rc, CURLcode result)
150
461
{
151
461
  switch(rc) {
152
3
  case LDAP_NO_MEMORY:
153
3
    return CURLE_OUT_OF_MEMORY;
154
2
  case LDAP_INVALID_CREDENTIALS:
155
2
    return CURLE_LOGIN_DENIED;
156
3
  case LDAP_PROTOCOL_ERROR:
157
3
    return CURLE_UNSUPPORTED_PROTOCOL;
158
2
  case LDAP_INSUFFICIENT_ACCESS:
159
2
    return CURLE_REMOTE_ACCESS_DENIED;
160
461
  }
161
451
  return result;
162
461
}
163
164
static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
165
2.16k
{
166
2.16k
  CURLcode result = CURLE_OK;
167
2.16k
  int rc;
168
2.16k
  static const char * const url_errs[] = {
169
2.16k
    "success",
170
2.16k
    "out of memory",
171
2.16k
    "bad parameter",
172
2.16k
    "unrecognized scheme",
173
2.16k
    "unbalanced delimiter",
174
2.16k
    "bad URL",
175
2.16k
    "bad host or port",
176
2.16k
    "bad or missing attributes",
177
2.16k
    "bad or missing scope",
178
2.16k
    "bad or missing filter",
179
2.16k
    "bad or missing extensions"
180
2.16k
  };
181
182
2.16k
  *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
2.16k
  if((data->state.creds && (data->state.creds->source == CREDS_URL)) ||
188
2.15k
     data->state.up.options)
189
4
    rc = LDAP_URL_ERR_BADURL;
190
2.15k
  else
191
2.15k
    rc = ldap_url_parse(Curl_bufref_ptr(&data->state.url), ludp);
192
193
2.16k
  if(rc != LDAP_URL_SUCCESS) {
194
38
    const char *msg = "url parsing problem";
195
196
38
    result = rc == LDAP_URL_ERR_MEM ? CURLE_OUT_OF_MEMORY :
197
38
      CURLE_URL_MALFORMAT;
198
38
    rc -= LDAP_URL_SUCCESS;
199
38
    if((size_t)rc < CURL_ARRAYSIZE(url_errs))
200
38
      msg = url_errs[rc];
201
38
    failf(data, "LDAP local: %s", msg);
202
38
  }
203
2.16k
  return result;
204
2.16k
}
205
206
/* Parse the login options. */
207
static CURLcode oldap_parse_login_options(struct connectdata *conn)
208
1.30k
{
209
1.30k
  CURLcode result = CURLE_OK;
210
1.30k
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
211
1.30k
  const char *ptr = conn->options;
212
213
1.30k
  DEBUGASSERT(li);
214
1.30k
  if(!li)
215
0
    return CURLE_FAILED_INIT;
216
217
1.32k
  while(!result && ptr && *ptr) {
218
18
    const char *key = ptr;
219
18
    const char *value;
220
221
291
    while(*ptr && *ptr != '=')
222
273
      ptr++;
223
224
18
    value = ptr + 1;
225
226
236
    while(*ptr && *ptr != ';')
227
218
      ptr++;
228
229
18
    if(checkprefix("AUTH=", key))
230
0
      result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value);
231
18
    else
232
18
      result = CURLE_SETOPT_OPTION_SYNTAX;
233
234
18
    if(*ptr == ';')
235
1
      ptr++;
236
18
  }
237
238
1.30k
  return result == CURLE_URL_MALFORMAT ? CURLE_SETOPT_OPTION_SYNTAX : result;
239
1.30k
}
240
241
static CURLcode oldap_setup_connection(struct Curl_easy *data,
242
                                       struct connectdata *conn)
243
1.39k
{
244
1.39k
  CURLcode result;
245
1.39k
  LDAPURLDesc *lud;
246
1.39k
  (void)conn;
247
248
  /* Early URL syntax check. */
249
1.39k
  result = oldap_url_parse(data, &lud);
250
1.39k
  ldap_free_urldesc(lud);
251
252
1.39k
  return result;
253
1.39k
}
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
1.23k
{
344
1.23k
  struct connectdata *conn = data->conn;
345
1.23k
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
346
1.23k
  const char *binddn = NULL;
347
1.23k
  struct berval passwd;
348
1.23k
  int rc;
349
350
1.23k
  if(!li)
351
0
    return CURLE_FAILED_INIT;
352
1.23k
  passwd.bv_val = NULL;
353
1.23k
  passwd.bv_len = 0;
354
355
1.23k
  if(conn->creds) {
356
61
    binddn = Curl_creds_user(conn->creds);
357
61
    passwd.bv_val = CURL_UNCONST(Curl_creds_passwd(conn->creds));
358
61
    passwd.bv_len = strlen(passwd.bv_val);
359
61
  }
360
361
1.23k
  rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
362
1.23k
                      NULL, NULL, &li->msgid);
363
1.23k
  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
1.23k
  oldap_state(data, li, newstate);
368
1.23k
  return CURLE_OK;
369
1.23k
}
370
371
/* Query the supported SASL authentication mechanisms. */
372
static CURLcode oldap_perform_mechs(struct Curl_easy *data)
373
58
{
374
58
  struct ldapconninfo *li =
375
58
    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
376
58
  int rc;
377
58
  static const char * const supportedSASLMechanisms[] = {
378
58
    "supportedSASLMechanisms",
379
58
    NULL
380
58
  };
381
382
58
  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
58
  rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
389
58
                       (char **)CURL_UNCONST(supportedSASLMechanisms), 0,
390
58
                       NULL, NULL, NULL, 0, &li->msgid);
391
58
  if(rc != LDAP_SUCCESS)
392
0
    return oldap_map_error(rc, CURLE_LOGIN_DENIED);
393
58
  oldap_state(data, li, OLDAP_MECHS);
394
58
  return CURLE_OK;
395
58
}
396
397
/* Starts SASL bind. */
398
static CURLcode oldap_perform_sasl(struct Curl_easy *data)
399
1
{
400
1
  struct ldapconninfo *li =
401
1
    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
402
1
  saslprogress progress = SASL_IDLE;
403
1
  CURLcode result;
404
405
1
  if(!li)
406
0
    return CURLE_FAILED_INIT;
407
1
  result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
408
409
1
  oldap_state(data, li, OLDAP_SASL);
410
1
  if(!result && progress != SASL_INPROGRESS)
411
1
    result = Curl_sasl_is_blocked(&li->sasl, data);
412
1
  return result;
413
1
}
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
2.07k
{
508
2.07k
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
509
2.07k
  return li && li->recv;
510
2.07k
}
511
512
static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
513
2
{
514
2
  struct connectdata *conn = data->conn;
515
2
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
516
2
  bool ssldone = FALSE;
517
2
  CURLcode result;
518
519
2
  if(!li)
520
0
    return CURLE_FAILED_INIT;
521
2
  result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
522
2
  if(result)
523
2
    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
19
{
543
19
  struct ldapconninfo *li =
544
19
    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
545
19
  int rc;
546
547
19
  if(!li)
548
0
    return CURLE_FAILED_INIT;
549
19
  rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
550
19
  if(rc != LDAP_SUCCESS)
551
0
    return oldap_map_error(rc, CURLE_USE_SSL_FAILED);
552
19
  oldap_state(data, li, OLDAP_STARTTLS);
553
19
  return CURLE_OK;
554
19
}
555
#endif
556
557
static void oldap_easy_dtor(const void *key, size_t klen, void *entry)
558
469
{
559
469
  struct ldapreqinfo *lr = entry;
560
469
  (void)key;
561
469
  (void)klen;
562
469
  curlx_free(lr);
563
469
}
564
565
static void oldap_conn_dtor(const void *key, size_t klen, void *entry)
566
1.30k
{
567
1.30k
  struct ldapconninfo *li = entry;
568
1.30k
  (void)key;
569
1.30k
  (void)klen;
570
1.30k
  if(li->ld) {
571
0
    ldap_unbind_ext(li->ld, NULL, NULL);
572
0
    li->ld = NULL;
573
0
  }
574
1.30k
  curlx_free(li);
575
1.30k
}
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
1.30k
{
593
1.30k
  struct connectdata *conn = data->conn;
594
1.30k
  struct ldapconninfo *li;
595
1.30k
  static const int version = LDAP_VERSION3;
596
1.30k
  char *hosturl = NULL;
597
1.30k
  CURLcode result;
598
1.30k
  int rc;
599
#ifdef CURL_OPENLDAP_DEBUG
600
  static int do_trace = -1;
601
#endif
602
603
1.30k
  (void)done;
604
605
1.30k
  li = curlx_calloc(1, sizeof(struct ldapconninfo));
606
1.30k
  if(!li) {
607
0
    result = CURLE_OUT_OF_MEMORY;
608
0
    goto out;
609
0
  }
610
611
1.30k
  result = Curl_conn_meta_set(conn, CURL_META_LDAP_CONN, li, oldap_conn_dtor);
612
1.30k
  if(result)
613
0
    goto out;
614
615
1.30k
  li->proto = ldap_pvt_url_scheme2proto(data->state.origin->scheme->name);
616
617
  /* Initialize the SASL storage */
618
1.30k
  Curl_sasl_init(&li->sasl, data, &saslldap);
619
620
1.30k
  result = oldap_parse_login_options(conn);
621
1.30k
  if(result)
622
18
    goto out;
623
624
1.28k
  hosturl = curl_maprintf("%s://%s:%u",
625
1.28k
                          conn->scheme->name,
626
1.28k
                          conn->origin->ipv6 ?
627
0
                          conn->origin->user_hostname :
628
1.28k
                          conn->origin->hostname,
629
1.28k
                          conn->origin->port);
630
1.28k
  if(!hosturl) {
631
0
    result = CURLE_OUT_OF_MEMORY;
632
0
    goto out;
633
0
  }
634
635
1.28k
  rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
636
1.28k
  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
1.28k
  ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
656
657
  /* Do not chase referrals. */
658
1.28k
  ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
659
660
1.28k
  {
661
1.28k
    ber_len_t max = 256 * 1024;
662
1.28k
    Sockbuf *sb;
663
1.28k
    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
1.28k
       !ber_sockbuf_ctrl(sb, LBER_SB_OPT_SET_MAX_INCOMING, &max)) {
668
0
      result = CURLE_FAILED_INIT;
669
0
      goto out;
670
0
    }
671
1.28k
  }
672
673
1.28k
#ifdef USE_SSL
674
1.28k
  if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
675
0
    result = oldap_ssl_connect(data, OLDAP_SSL);
676
0
    goto out;
677
0
  }
678
679
1.28k
  if(data->set.use_ssl) {
680
19
    result = oldap_perform_starttls(data);
681
19
    if(!result || data->set.use_ssl != CURLUSESSL_TRY)
682
19
      goto out;
683
19
  }
684
1.27k
#endif
685
686
1.27k
  if(li->sasl.prefmech != SASL_AUTH_NONE) {
687
56
    result = oldap_perform_mechs(data);
688
56
    goto out;
689
56
  }
690
691
  /* Force bind even if anonymous bind is not needed in protocol version 3
692
     to detect missing version 3 support. */
693
1.21k
  result = oldap_perform_bind(data, OLDAP_BIND);
694
695
1.30k
out:
696
1.30k
  curlx_free(hosturl);
697
1.30k
  return result;
698
1.21k
}
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
56
{
704
56
  struct connectdata *conn;
705
56
  struct ldapconninfo *li;
706
56
  int rc;
707
56
  BerElement *ber = NULL;
708
56
  CURLcode result = CURLE_OK;
709
56
  struct berval bv, *bvals;
710
711
56
  if(!data)
712
0
    return CURLE_FAILED_INIT;
713
714
56
  conn = data->conn;
715
56
  li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
716
717
56
  if(!li)
718
0
    return CURLE_FAILED_INIT;
719
56
  switch(ldap_msgtype(msg)) {
720
18
  case LDAP_RES_SEARCH_ENTRY:
721
    /* Got a list of supported SASL mechanisms. */
722
18
    if(code != LDAP_SUCCESS && code != LDAP_NO_RESULTS_RETURNED)
723
0
      return CURLE_LOGIN_DENIED;
724
725
18
    rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
726
18
    if(rc < 0)
727
1
      return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING);
728
17
    for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
729
41
        rc == LDAP_SUCCESS;
730
28
        rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
731
28
      int i;
732
733
28
      if(!bv.bv_val)
734
4
        break;
735
736
24
      if(bvals) {
737
45
        for(i = 0; bvals[i].bv_val; i++) {
738
36
          size_t llen;
739
36
          unsigned short mech =
740
36
            Curl_sasl_decode_mech((const char *)bvals[i].bv_val,
741
36
                                  bvals[i].bv_len, &llen);
742
36
          if(bvals[i].bv_len == llen)
743
0
            li->sasl.authmechs |= mech;
744
36
        }
745
9
        ber_memfree(bvals);
746
9
      }
747
24
    }
748
17
    ber_free(ber, 0);
749
17
    break;
750
751
8
  case LDAP_RES_SEARCH_RESULT:
752
8
    switch(code) {
753
1
    case LDAP_SIZELIMIT_EXCEEDED:
754
1
      infof(data, "Too many authentication mechanisms");
755
1
      FALLTHROUGH();
756
3
    case LDAP_SUCCESS:
757
4
    case LDAP_NO_RESULTS_RETURNED:
758
4
      if(Curl_sasl_can_authenticate(&li->sasl, data))
759
1
        result = oldap_perform_sasl(data);
760
3
      else
761
3
        result = CURLE_LOGIN_DENIED;
762
4
      break;
763
4
    default:
764
4
      result = oldap_map_error(code, CURLE_LOGIN_DENIED);
765
4
      break;
766
8
    }
767
8
    break;
768
30
  default:
769
30
    break;
770
56
  }
771
55
  return result;
772
56
}
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
840
{
818
840
  struct connectdata *conn = data->conn;
819
840
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
820
840
  CURLcode result = CURLE_OK;
821
840
  struct berval *bv = NULL;
822
840
  int rc;
823
824
840
  if(!li)
825
0
    return CURLE_FAILED_INIT;
826
827
840
  if(code != LDAP_SUCCESS)
828
56
    return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND);
829
830
784
  rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0);
831
784
  if(rc != LDAP_SUCCESS) {
832
14
    failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s",
833
14
          ldap_err2string(rc));
834
14
    result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
835
14
  }
836
770
  else
837
770
    oldap_state(data, li, OLDAP_STOP);
838
839
784
  if(bv)
840
18
    ber_bvfree(bv);
841
784
  return result;
842
840
}
843
844
static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
845
1.55k
{
846
1.55k
  CURLcode result = CURLE_OK;
847
1.55k
  struct connectdata *conn = data->conn;
848
1.55k
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
849
1.55k
  LDAPMessage *msg = NULL;
850
1.55k
  struct timeval tv = { 0, 0 };
851
1.55k
  int code = LDAP_SUCCESS;
852
1.55k
  int rc;
853
854
1.55k
  if(!li)
855
0
    return CURLE_FAILED_INIT;
856
857
1.55k
  if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) {
858
    /* Get response to last command. */
859
1.55k
    rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg);
860
1.55k
    switch(rc) {
861
243
    case 0:                               /* Timed out. */
862
243
      return CURLE_OK;
863
23
    case LDAP_RES_SEARCH_ENTRY:
864
31
    case LDAP_RES_SEARCH_REFERENCE:
865
31
      break;
866
1.27k
    default:
867
1.27k
      li->msgid = 0;                      /* Nothing to abandon upon error. */
868
1.27k
      if(rc < 0) {
869
380
        failf(data, "LDAP local: connecting ldap_result %s",
870
380
              ldap_err2string(rc));
871
380
        return oldap_map_error(rc, CURLE_COULDNT_CONNECT);
872
380
      }
873
898
      break;
874
1.55k
    }
875
876
    /* Get error code from message. */
877
929
    rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0);
878
929
    if(rc)
879
78
      code = rc;
880
851
    else {
881
      /* store the latest code for later retrieval */
882
851
      data->info.httpcode = code;
883
851
    }
884
885
    /* If protocol version 3 is not supported, fallback to version 2. */
886
929
    if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 &&
887
18
#ifdef USE_SSL
888
18
       (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) &&
889
17
#endif
890
17
       li->sasl.prefmech == SASL_AUTH_NONE) {
891
15
      static const int version = LDAP_VERSION2;
892
893
15
      ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
894
15
      ldap_msgfree(msg);
895
15
      return oldap_perform_bind(data, OLDAP_BINDV2);
896
15
    }
897
929
  }
898
899
  /* Handle response message according to current state. */
900
914
  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
18
  case OLDAP_STARTTLS:
913
18
    if(code != LDAP_SUCCESS) {
914
16
      if(data->set.use_ssl != CURLUSESSL_TRY)
915
4
        result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
916
12
      else if(li->sasl.prefmech != SASL_AUTH_NONE)
917
2
        result = oldap_perform_mechs(data);
918
10
      else
919
10
        result = oldap_perform_bind(data, OLDAP_BIND);
920
16
      break;
921
16
    }
922
2
    result = Curl_ssl_cfilter_add(
923
2
      data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET);
924
2
    if(result)
925
0
      break;
926
2
    FALLTHROUGH();
927
2
  case OLDAP_TLS:
928
2
    result = oldap_ssl_connect(data, OLDAP_TLS);
929
2
    if(result)
930
2
      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
2
    break;
943
0
#endif
944
945
56
  case OLDAP_MECHS:
946
56
    result = oldap_state_mechs_resp(data, msg, code);
947
56
    break;
948
0
  case OLDAP_SASL:
949
0
    result = oldap_state_sasl_resp(data, msg, code);
950
0
    break;
951
834
  case OLDAP_BIND:
952
840
  case OLDAP_BINDV2:
953
840
    result = oldap_state_bind_resp(data, msg, code);
954
840
    break;
955
0
  default:
956
    /* internal error */
957
0
    result = CURLE_COULDNT_CONNECT;
958
0
    break;
959
914
  }
960
961
914
  ldap_msgfree(msg);
962
963
914
  *done = li->state == OLDAP_STOP;
964
914
  if(*done)
965
770
    conn->recv[FIRSTSOCKET] = oldap_recv;
966
967
914
  if(result && li->msgid) {
968
5
    ldap_abandon_ext(li->ld, li->msgid, NULL, NULL);
969
5
    li->msgid = 0;
970
5
  }
971
914
  return result;
972
914
}
973
974
static CURLcode oldap_disconnect(struct Curl_easy *data,
975
                                 struct connectdata *conn,
976
                                 bool dead_connection)
977
1.39k
{
978
1.39k
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
979
1.39k
  (void)dead_connection;
980
#ifndef USE_SSL
981
  (void)data;
982
#endif
983
984
1.39k
  if(li && li->ld) {
985
1.28k
#ifdef USE_SSL
986
1.28k
    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
1.28k
#endif
993
1.28k
    ldap_unbind_ext(li->ld, NULL, NULL);
994
1.28k
    li->ld = NULL;
995
1.28k
  }
996
1.39k
  return CURLE_OK;
997
1.39k
}
998
999
static CURLcode oldap_do(struct Curl_easy *data, bool *done)
1000
770
{
1001
770
  struct connectdata *conn = data->conn;
1002
770
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
1003
770
  struct ldapreqinfo *lr;
1004
770
  CURLcode result;
1005
770
  int rc;
1006
770
  LDAPURLDesc *lud;
1007
770
  int msgid;
1008
1009
770
  if(!li)
1010
0
    return CURLE_FAILED_INIT;
1011
1012
770
  infof(data, "LDAP local: %s", Curl_bufref_ptr(&data->state.url));
1013
1014
770
  result = oldap_url_parse(data, &lud);
1015
770
  if(result)
1016
0
    goto out;
1017
1018
770
#ifdef USE_SSL
1019
770
  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
770
#endif
1029
1030
770
  rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
1031
770
                       lud->lud_filter, lud->lud_attrs, 0,
1032
770
                       NULL, NULL, NULL, 0, &msgid);
1033
770
  ldap_free_urldesc(lud);
1034
770
  if(rc != LDAP_SUCCESS) {
1035
301
    failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
1036
301
    result = CURLE_LDAP_SEARCH_FAILED;
1037
301
    goto out;
1038
301
  }
1039
1040
469
  lr = curlx_calloc(1, sizeof(struct ldapreqinfo));
1041
469
  if(!lr ||
1042
469
     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
469
  lr->msgid = msgid;
1049
469
  Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
1050
469
  *done = TRUE;
1051
1052
770
out:
1053
770
  return result;
1054
469
}
1055
1056
static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
1057
                           bool premature)
1058
1.30k
{
1059
1.30k
  struct connectdata *conn = data->conn;
1060
1.30k
  struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY);
1061
1062
1.30k
  (void)res;
1063
1.30k
  (void)premature;
1064
1065
1.30k
  if(lr) {
1066
    /* if there was a search in progress, abandon it */
1067
469
    if(lr->msgid) {
1068
362
      struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
1069
362
      if(li && li->ld) {
1070
362
        ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
1071
362
      }
1072
362
      lr->msgid = 0;
1073
362
    }
1074
469
    Curl_meta_remove(data, CURL_META_LDAP_EASY);
1075
469
  }
1076
1077
1.30k
  return CURLE_OK;
1078
1.30k
}
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
375
{
1085
375
  CURLcode result = CURLE_OK;
1086
1087
375
  if(prefix) {
1088
    /* If we have a zero-length value and the prefix ends with a space
1089
       separator, drop the latter. */
1090
375
    if(!len && plen && prefix[plen - 1] == ' ')
1091
124
      plen--;
1092
375
    result = Curl_client_write(data, CLIENTWRITE_BODY, prefix, plen);
1093
375
  }
1094
375
  if(!result && value) {
1095
258
    result = Curl_client_write(data, CLIENTWRITE_BODY, value, len);
1096
258
  }
1097
375
  if(!result && suffix) {
1098
258
    result = Curl_client_write(data, CLIENTWRITE_BODY, suffix, slen);
1099
258
  }
1100
375
  return result;
1101
375
}
1102
1103
static CURLcode oldap_recv(struct Curl_easy *data, int8_t sockindex, char *buf,
1104
                           size_t len, size_t *pnread)
1105
709
{
1106
709
  struct connectdata *conn = data->conn;
1107
709
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
1108
709
  struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY);
1109
709
  int rc;
1110
709
  LDAPMessage *msg = NULL;
1111
709
  BerElement *ber = NULL;
1112
709
  struct timeval tv = { 0, 0 };
1113
709
  struct berval bv, *bvals;
1114
709
  CURLcode result = CURLE_AGAIN;
1115
709
  int code;
1116
709
  char *info = NULL;
1117
1118
709
  (void)len;
1119
709
  (void)buf;
1120
709
  (void)sockindex;
1121
709
  *pnread = 0;
1122
709
  if(!li || !lr)
1123
0
    return CURLE_FAILED_INIT;
1124
1125
709
  rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
1126
709
  if(rc < 0) {
1127
321
    failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
1128
321
    result = CURLE_RECV_ERROR;
1129
321
  }
1130
1131
  /* error or timed out */
1132
709
  if(!msg)
1133
505
    return result;
1134
1135
204
  result = CURLE_OK;
1136
1137
204
  switch(ldap_msgtype(msg)) {
1138
107
  case LDAP_RES_SEARCH_RESULT:
1139
107
    lr->msgid = 0;
1140
107
    rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0);
1141
107
    if(rc) {
1142
11
      failf(data, "LDAP local: search ldap_parse_result %s",
1143
11
            ldap_err2string(rc));
1144
11
      result = CURLE_LDAP_SEARCH_FAILED;
1145
11
      break;
1146
11
    }
1147
1148
    /* store the latest code for later retrieval */
1149
96
    data->info.httpcode = code;
1150
1151
96
    switch(code) {
1152
1
    case LDAP_SIZELIMIT_EXCEEDED:
1153
1
      infof(data, "There are more than %d entries", lr->nument);
1154
1
      FALLTHROUGH();
1155
3
    case LDAP_SUCCESS:
1156
3
      data->req.size = data->req.bytecount;
1157
3
      break;
1158
93
    default:
1159
93
      failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code),
1160
93
            info ? info : "");
1161
93
      result = CURLE_LDAP_SEARCH_FAILED;
1162
93
      break;
1163
96
    }
1164
96
    if(info)
1165
36
      ldap_memfree(info);
1166
96
    break;
1167
86
  case LDAP_RES_SEARCH_ENTRY:
1168
86
    lr->nument++;
1169
86
    rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
1170
86
    if(rc < 0) {
1171
2
      result = CURLE_RECV_ERROR;
1172
2
      break;
1173
2
    }
1174
1175
84
    result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len,
1176
84
                          STRCONST("\n"));
1177
84
    if(result)
1178
2
      break;
1179
1180
82
    for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
1181
133
        rc == LDAP_SUCCESS;
1182
94
        rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
1183
94
      int i;
1184
94
      bool binary;
1185
1186
94
      if(!bv.bv_val)
1187
38
        break;
1188
1189
56
      if(!bvals) {
1190
18
        result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1191
18
                              STRCONST(":\n"));
1192
18
        if(result)
1193
1
          break;
1194
17
        continue;
1195
18
      }
1196
1197
38
      binary = bv.bv_len > 7 &&
1198
1
        curl_strnequal(bv.bv_val + bv.bv_len - 7, ";binary", 7);
1199
1200
116
      for(i = 0; bvals[i].bv_val; i++) {
1201
81
        bool binval = FALSE;
1202
1203
81
        result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1204
81
                              STRCONST(":"));
1205
81
        if(result)
1206
1
          break;
1207
1208
80
        if(!binary) {
1209
          /* check for a leading ':' or '<' (not a SAFE-INIT-CHAR per RFC
1210
             2849) or leading or trailing whitespace */
1211
80
          if(bvals[i].bv_len &&
1212
35
             ((bvals[i].bv_val[0] == ':') || (bvals[i].bv_val[0] == '<') ||
1213
24
              ISBLANK(bvals[i].bv_val[0]) ||
1214
15
              ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1])))
1215
23
            binval = TRUE;
1216
57
          else {
1217
            /* check for unprintable characters */
1218
57
            unsigned int j;
1219
74
            for(j = 0; j < bvals[i].bv_len; j++)
1220
24
              if(!ISPRINT(bvals[i].bv_val[j])) {
1221
7
                binval = TRUE;
1222
7
                break;
1223
7
              }
1224
57
          }
1225
80
        }
1226
80
        if(binary || binval) {
1227
30
          char *val_b64 = NULL;
1228
30
          size_t val_b64_sz = 0;
1229
1230
          /* Binary value, encode to base64. */
1231
30
          if(bvals[i].bv_len)
1232
30
            result = curlx_base64_encode((uint8_t *)bvals[i].bv_val,
1233
30
                                         bvals[i].bv_len,
1234
30
                                         &val_b64, &val_b64_sz);
1235
30
          if(!result)
1236
30
            result = client_write(data, STRCONST(": "), val_b64, val_b64_sz,
1237
30
                                  STRCONST("\n"));
1238
30
          curlx_free(val_b64);
1239
30
        }
1240
50
        else
1241
50
          result = client_write(data, STRCONST(" "),
1242
50
                                bvals[i].bv_val, bvals[i].bv_len,
1243
50
                                STRCONST("\n"));
1244
80
        if(result)
1245
2
          break;
1246
80
      }
1247
1248
38
      ber_memfree(bvals);
1249
38
      bvals = NULL;
1250
38
      if(!result)
1251
35
        result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1252
38
      if(result)
1253
4
        break;
1254
38
    }
1255
1256
82
    if(!result)
1257
77
      result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1258
82
    if(!result)
1259
76
      result = CURLE_AGAIN;
1260
82
    break;
1261
204
  }
1262
1263
204
  ber_free(ber, 0);
1264
204
  ldap_msgfree(msg);
1265
204
  return result;
1266
204
}
1267
1268
void Curl_ldap_version(char *buf, size_t bufsz)
1269
0
{
1270
0
  LDAPAPIInfo api;
1271
0
  api.ldapai_info_version = LDAP_API_INFO_VERSION;
1272
1273
0
  if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) == LDAP_OPT_SUCCESS) {
1274
0
    unsigned int patch = (unsigned int)(api.ldapai_vendor_version % 100);
1275
0
    unsigned int major = (unsigned int)(api.ldapai_vendor_version / 10000);
1276
0
    unsigned int minor =
1277
0
      (((unsigned int)api.ldapai_vendor_version - (major * 10000))
1278
0
       - patch) / 100;
1279
0
    curl_msnprintf(buf, bufsz, "%s/%u.%u.%u",
1280
0
                   api.ldapai_vendor_name, major, minor, patch);
1281
0
    ldap_memfree(api.ldapai_vendor_name);
1282
0
    ber_memvfree((void **)api.ldapai_extensions);
1283
0
  }
1284
0
  else
1285
0
    curl_msnprintf(buf, bufsz, "OpenLDAP");
1286
0
}
1287
1288
/*
1289
 * LDAP protocol handler.
1290
 */
1291
const struct Curl_protocol Curl_protocol_ldap = {
1292
  oldap_setup_connection,               /* setup_connection */
1293
  oldap_do,                             /* do_it */
1294
  oldap_done,                           /* done */
1295
  ZERO_NULL,                            /* do_more */
1296
  oldap_connect,                        /* connect_it */
1297
  oldap_connecting,                     /* connecting */
1298
  ZERO_NULL,                            /* doing */
1299
  ZERO_NULL,                            /* proto_pollset */
1300
  ZERO_NULL,                            /* doing_pollset */
1301
  ZERO_NULL,                            /* domore_pollset */
1302
  ZERO_NULL,                            /* perform_pollset */
1303
  oldap_disconnect,                     /* disconnect */
1304
  ZERO_NULL,                            /* write_resp */
1305
  ZERO_NULL,                            /* write_resp_hd */
1306
  ZERO_NULL,                            /* connection_is_dead */
1307
  ZERO_NULL,                            /* attach connection */
1308
  ZERO_NULL,                            /* follow */
1309
};
1310
1311
#endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */
1312
1313
/* The LDAP scheme structs are in ldap.c */