Coverage Report

Created: 2026-04-12 06:59

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
6.65k
#define CURL_META_LDAP_EASY   "meta:proto:ldap:easy"
114
/* meta key for storing ldapconninfo at connection */
115
33.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
4.46k
{
125
4.46k
#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
126
  /* for debug purposes */
127
4.46k
  static const char * const names[] = {
128
4.46k
    "STOP",
129
4.46k
    "SSL",
130
4.46k
    "STARTTLS",
131
4.46k
    "TLS",
132
4.46k
    "MECHS",
133
4.46k
    "SASL",
134
4.46k
    "BIND",
135
4.46k
    "BINDV2",
136
    /* LAST */
137
4.46k
  };
138
139
4.46k
  if(li->state != newstate)
140
4.46k
    infof(data, "LDAP %p state change from %s to %s",
141
4.46k
          (void *)li, names[li->state], names[newstate]);
142
#else
143
  (void)data;
144
#endif
145
4.46k
  li->state = newstate;
146
4.46k
}
147
148
/* Map some particular LDAP error codes to CURLcode values. */
149
static CURLcode oldap_map_error(int rc, CURLcode result)
150
1.96k
{
151
1.96k
  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
4
  case LDAP_PROTOCOL_ERROR:
157
4
    return CURLE_UNSUPPORTED_PROTOCOL;
158
3
  case LDAP_INSUFFICIENT_ACCESS:
159
3
    return CURLE_REMOTE_ACCESS_DENIED;
160
1.96k
  }
161
1.95k
  return result;
162
1.96k
}
163
164
static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
165
8.56k
{
166
8.56k
  CURLcode result = CURLE_OK;
167
8.56k
  int rc = LDAP_URL_ERR_BADURL;
168
8.56k
  static const char * const url_errs[] = {
169
8.56k
    "success",
170
8.56k
    "out of memory",
171
8.56k
    "bad parameter",
172
8.56k
    "unrecognized scheme",
173
8.56k
    "unbalanced delimiter",
174
8.56k
    "bad URL",
175
8.56k
    "bad host or port",
176
8.56k
    "bad or missing attributes",
177
8.56k
    "bad or missing scope",
178
8.56k
    "bad or missing filter",
179
8.56k
    "bad or missing extensions"
180
8.56k
  };
181
182
8.56k
  *ludp = NULL;
183
8.56k
  if(!data->state.up.user && !data->state.up.password &&
184
8.48k
     !data->state.up.options)
185
8.48k
    rc = ldap_url_parse(Curl_bufref_ptr(&data->state.url), ludp);
186
8.56k
  if(rc != LDAP_URL_SUCCESS) {
187
179
    const char *msg = "url parsing problem";
188
189
179
    result = rc == LDAP_URL_ERR_MEM ? CURLE_OUT_OF_MEMORY :
190
179
      CURLE_URL_MALFORMAT;
191
179
    rc -= LDAP_URL_SUCCESS;
192
179
    if((size_t)rc < CURL_ARRAYSIZE(url_errs))
193
179
      msg = url_errs[rc];
194
179
    failf(data, "LDAP local: %s", msg);
195
179
  }
196
8.56k
  return result;
197
8.56k
}
198
199
/* Parse the login options. */
200
static CURLcode oldap_parse_login_options(struct connectdata *conn)
201
3.22k
{
202
3.22k
  CURLcode result = CURLE_OK;
203
3.22k
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
204
3.22k
  const char *ptr = conn->options;
205
206
3.22k
  DEBUGASSERT(li);
207
3.22k
  if(!li)
208
0
    return CURLE_FAILED_INIT;
209
210
3.24k
  while(!result && ptr && *ptr) {
211
18
    const char *key = ptr;
212
18
    const char *value;
213
214
234
    while(*ptr && *ptr != '=')
215
216
      ptr++;
216
217
18
    value = ptr + 1;
218
219
228
    while(*ptr && *ptr != ';')
220
210
      ptr++;
221
222
18
    if(checkprefix("AUTH=", key))
223
0
      result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value);
224
18
    else
225
18
      result = CURLE_SETOPT_OPTION_SYNTAX;
226
227
18
    if(*ptr == ';')
228
1
      ptr++;
229
18
  }
230
231
3.22k
  return result == CURLE_URL_MALFORMAT ? CURLE_SETOPT_OPTION_SYNTAX : result;
232
3.22k
}
233
234
static CURLcode oldap_setup_connection(struct Curl_easy *data,
235
                                       struct connectdata *conn)
236
7.33k
{
237
7.33k
  CURLcode result;
238
7.33k
  LDAPURLDesc *lud;
239
7.33k
  (void)conn;
240
241
  /* Early URL syntax check. */
242
7.33k
  result = oldap_url_parse(data, &lud);
243
7.33k
  ldap_free_urldesc(lud);
244
245
7.33k
  return result;
246
7.33k
}
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
2.90k
{
337
2.90k
  struct connectdata *conn = data->conn;
338
2.90k
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
339
2.90k
  const char *binddn = NULL;
340
2.90k
  struct berval passwd;
341
2.90k
  int rc;
342
343
2.90k
  if(!li)
344
0
    return CURLE_FAILED_INIT;
345
2.90k
  passwd.bv_val = NULL;
346
2.90k
  passwd.bv_len = 0;
347
348
2.90k
  if(data->state.aptr.user) {
349
79
    binddn = conn->user;
350
79
    passwd.bv_val = conn->passwd;
351
79
    passwd.bv_len = strlen(passwd.bv_val);
352
79
  }
353
354
2.90k
  rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
355
2.90k
                      NULL, NULL, &li->msgid);
356
2.90k
  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
2.90k
  oldap_state(data, li, newstate);
361
2.90k
  return CURLE_OK;
362
2.90k
}
363
364
/* Query the supported SASL authentication mechanisms. */
365
static CURLcode oldap_perform_mechs(struct Curl_easy *data)
366
283
{
367
283
  struct ldapconninfo *li =
368
283
    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
369
283
  int rc;
370
283
  static const char * const supportedSASLMechanisms[] = {
371
283
    "supportedSASLMechanisms",
372
283
    NULL
373
283
  };
374
375
283
  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
283
  rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
382
283
                       (char **)CURL_UNCONST(supportedSASLMechanisms), 0,
383
283
                       NULL, NULL, NULL, 0, &li->msgid);
384
283
  if(rc != LDAP_SUCCESS)
385
0
    return oldap_map_error(rc, CURLE_LOGIN_DENIED);
386
283
  oldap_state(data, li, OLDAP_MECHS);
387
283
  return CURLE_OK;
388
283
}
389
390
/* Starts SASL bind. */
391
static CURLcode oldap_perform_sasl(struct Curl_easy *data)
392
1
{
393
1
  struct ldapconninfo *li =
394
1
    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
395
1
  saslprogress progress = SASL_IDLE;
396
1
  CURLcode result;
397
398
1
  if(!li)
399
0
    return CURLE_FAILED_INIT;
400
1
  result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
401
402
1
  oldap_state(data, li, OLDAP_SASL);
403
1
  if(!result && progress != SASL_INPROGRESS)
404
1
    result = Curl_sasl_is_blocked(&li->sasl, data);
405
1
  return result;
406
1
}
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
4.45k
{
501
4.45k
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
502
4.45k
  return li && li->recv != NULL;
503
4.45k
}
504
505
static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
506
1
{
507
1
  struct connectdata *conn = data->conn;
508
1
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
509
1
  bool ssldone = FALSE;
510
1
  CURLcode result;
511
512
1
  if(!li)
513
0
    return CURLE_FAILED_INIT;
514
1
  result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
515
1
  if(result)
516
1
    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
45
{
536
45
  struct ldapconninfo *li =
537
45
    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
538
45
  int rc;
539
540
45
  if(!li)
541
0
    return CURLE_FAILED_INIT;
542
45
  rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
543
45
  if(rc != LDAP_SUCCESS)
544
0
    return oldap_map_error(rc, CURLE_USE_SSL_FAILED);
545
45
  oldap_state(data, li, OLDAP_STARTTLS);
546
45
  return CURLE_OK;
547
45
}
548
#endif
549
550
static void oldap_easy_dtor(void *key, size_t klen, void *entry)
551
870
{
552
870
  struct ldapreqinfo *lr = entry;
553
870
  (void)key;
554
870
  (void)klen;
555
870
  curlx_free(lr);
556
870
}
557
558
static void oldap_conn_dtor(void *key, size_t klen, void *entry)
559
3.22k
{
560
3.22k
  struct ldapconninfo *li = entry;
561
3.22k
  (void)key;
562
3.22k
  (void)klen;
563
3.22k
  if(li->ld) {
564
0
    ldap_unbind_ext(li->ld, NULL, NULL);
565
0
    li->ld = NULL;
566
0
  }
567
3.22k
  curlx_free(li);
568
3.22k
}
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
3.22k
{
586
3.22k
  struct connectdata *conn = data->conn;
587
3.22k
  struct ldapconninfo *li;
588
3.22k
  static const int version = LDAP_VERSION3;
589
3.22k
  char *hosturl = NULL;
590
3.22k
  CURLcode result;
591
3.22k
  int rc;
592
#ifdef CURL_OPENLDAP_DEBUG
593
  static int do_trace = -1;
594
#endif
595
596
3.22k
  (void)done;
597
598
3.22k
  li = curlx_calloc(1, sizeof(struct ldapconninfo));
599
3.22k
  if(!li) {
600
0
    result = CURLE_OUT_OF_MEMORY;
601
0
    goto out;
602
0
  }
603
604
3.22k
  result = Curl_conn_meta_set(conn, CURL_META_LDAP_CONN, li, oldap_conn_dtor);
605
3.22k
  if(result)
606
0
    goto out;
607
608
3.22k
  li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
609
610
  /* Initialize the SASL storage */
611
3.22k
  Curl_sasl_init(&li->sasl, data, &saslldap);
612
613
3.22k
  result = oldap_parse_login_options(conn);
614
3.22k
  if(result)
615
18
    goto out;
616
617
3.20k
  hosturl = curl_maprintf("%s://%s:%d",
618
3.20k
                          conn->scheme->name,
619
3.20k
                          (data->state.up.hostname[0] == '[') ?
620
3.18k
                          data->state.up.hostname : conn->host.name,
621
3.20k
                          conn->remote_port);
622
3.20k
  if(!hosturl) {
623
0
    result = CURLE_OUT_OF_MEMORY;
624
0
    goto out;
625
0
  }
626
627
3.20k
  rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
628
3.20k
  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
3.20k
  ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
648
649
  /* Do not chase referrals. */
650
3.20k
  ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
651
652
3.20k
  {
653
3.20k
    ber_len_t max = 256 * 1024;
654
3.20k
    Sockbuf *sb;
655
3.20k
    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
3.20k
       !ber_sockbuf_ctrl(sb, LBER_SB_OPT_SET_MAX_INCOMING, &max)) {
660
0
      result = CURLE_FAILED_INIT;
661
0
      goto out;
662
0
    }
663
3.20k
  }
664
665
3.20k
#ifdef USE_SSL
666
3.20k
  if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
667
0
    result = oldap_ssl_connect(data, OLDAP_SSL);
668
0
    goto out;
669
0
  }
670
671
3.20k
  if(data->set.use_ssl) {
672
45
    result = oldap_perform_starttls(data);
673
45
    if(!result || data->set.use_ssl != CURLUSESSL_TRY)
674
45
      goto out;
675
45
  }
676
3.16k
#endif
677
678
3.16k
  if(li->sasl.prefmech != SASL_AUTH_NONE) {
679
278
    result = oldap_perform_mechs(data);
680
278
    goto out;
681
278
  }
682
683
  /* Force bind even if anonymous bind is not needed in protocol version 3
684
     to detect missing version 3 support. */
685
2.88k
  result = oldap_perform_bind(data, OLDAP_BIND);
686
687
3.22k
out:
688
3.22k
  curlx_free(hosturl);
689
3.22k
  return result;
690
2.88k
}
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
319
{
696
319
  struct connectdata *conn;
697
319
  struct ldapconninfo *li;
698
319
  int rc;
699
319
  BerElement *ber = NULL;
700
319
  CURLcode result = CURLE_OK;
701
319
  struct berval bv, *bvals;
702
703
319
  if(!data)
704
0
    return CURLE_FAILED_INIT;
705
706
319
  conn = data->conn;
707
319
  li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
708
709
319
  if(!li)
710
0
    return CURLE_FAILED_INIT;
711
319
  switch(ldap_msgtype(msg)) {
712
175
  case LDAP_RES_SEARCH_ENTRY:
713
    /* Got a list of supported SASL mechanisms. */
714
175
    if(code != LDAP_SUCCESS && code != LDAP_NO_RESULTS_RETURNED)
715
0
      return CURLE_LOGIN_DENIED;
716
717
175
    rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
718
175
    if(rc < 0)
719
7
      return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING);
720
168
    for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
721
292
        rc == LDAP_SUCCESS;
722
207
        rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
723
207
      int i;
724
725
207
      if(!bv.bv_val)
726
83
        break;
727
728
124
      if(bvals) {
729
164
        for(i = 0; bvals[i].bv_val; i++) {
730
115
          size_t llen;
731
115
          unsigned short mech =
732
115
            Curl_sasl_decode_mech((const char *)bvals[i].bv_val,
733
115
                                  bvals[i].bv_len, &llen);
734
115
          if(bvals[i].bv_len == llen)
735
1
            li->sasl.authmechs |= mech;
736
115
        }
737
49
        ber_memfree(bvals);
738
49
      }
739
124
    }
740
168
    ber_free(ber, 0);
741
168
    break;
742
743
38
  case LDAP_RES_SEARCH_RESULT:
744
38
    switch(code) {
745
1
    case LDAP_SIZELIMIT_EXCEEDED:
746
1
      infof(data, "Too many authentication mechanisms");
747
1
      FALLTHROUGH();
748
3
    case LDAP_SUCCESS:
749
4
    case LDAP_NO_RESULTS_RETURNED:
750
4
      if(Curl_sasl_can_authenticate(&li->sasl, data))
751
1
        result = oldap_perform_sasl(data);
752
3
      else
753
3
        result = CURLE_LOGIN_DENIED;
754
4
      break;
755
34
    default:
756
34
      result = oldap_map_error(code, CURLE_LOGIN_DENIED);
757
34
      break;
758
38
    }
759
38
    break;
760
106
  default:
761
106
    break;
762
319
  }
763
312
  return result;
764
319
}
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
1.63k
{
799
1.63k
  struct connectdata *conn = data->conn;
800
1.63k
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
801
1.63k
  CURLcode result = CURLE_OK;
802
1.63k
  struct berval *bv = NULL;
803
1.63k
  int rc;
804
805
1.63k
  if(!li)
806
0
    return CURLE_FAILED_INIT;
807
808
1.63k
  if(code != LDAP_SUCCESS)
809
205
    return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND);
810
811
1.42k
  rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0);
812
1.42k
  if(rc != LDAP_SUCCESS) {
813
195
    failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s",
814
195
          ldap_err2string(rc));
815
195
    result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
816
195
  }
817
1.23k
  else
818
1.23k
    oldap_state(data, li, OLDAP_STOP);
819
820
1.42k
  if(bv)
821
2
    ber_bvfree(bv);
822
1.42k
  return result;
823
1.63k
}
824
825
static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
826
6.51k
{
827
6.51k
  CURLcode result = CURLE_OK;
828
6.51k
  struct connectdata *conn = data->conn;
829
6.51k
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
830
6.51k
  LDAPMessage *msg = NULL;
831
6.51k
  struct timeval tv = { 0, 0 };
832
6.51k
  int code = LDAP_SUCCESS;
833
6.51k
  int rc;
834
835
6.51k
  if(!li)
836
0
    return CURLE_FAILED_INIT;
837
838
6.51k
  if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) {
839
    /* Get response to last command. */
840
6.51k
    rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg);
841
6.51k
    switch(rc) {
842
3.01k
    case 0:                               /* Timed out. */
843
3.01k
      return CURLE_OK;
844
189
    case LDAP_RES_SEARCH_ENTRY:
845
191
    case LDAP_RES_SEARCH_REFERENCE:
846
191
      break;
847
3.30k
    default:
848
3.30k
      li->msgid = 0;                      /* Nothing to abandon upon error. */
849
3.30k
      if(rc < 0) {
850
1.49k
        failf(data, "LDAP local: connecting ldap_result %s",
851
1.49k
              ldap_err2string(rc));
852
1.49k
        return oldap_map_error(rc, CURLE_COULDNT_CONNECT);
853
1.49k
      }
854
1.81k
      break;
855
6.51k
    }
856
857
    /* Get error code from message. */
858
2.00k
    rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0);
859
2.00k
    if(rc)
860
322
      code = rc;
861
1.68k
    else {
862
      /* store the latest code for later retrieval */
863
1.68k
      data->info.httpcode = code;
864
1.68k
    }
865
866
    /* If protocol version 3 is not supported, fallback to version 2. */
867
2.00k
    if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 &&
868
18
#ifdef USE_SSL
869
18
       (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) &&
870
17
#endif
871
17
       li->sasl.prefmech == SASL_AUTH_NONE) {
872
9
      static const int version = LDAP_VERSION2;
873
874
9
      ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
875
9
      ldap_msgfree(msg);
876
9
      return oldap_perform_bind(data, OLDAP_BINDV2);
877
9
    }
878
2.00k
  }
879
880
  /* Handle response message according to current state. */
881
1.99k
  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
44
  case OLDAP_STARTTLS:
894
44
    if(code != LDAP_SUCCESS) {
895
43
      if(data->set.use_ssl != CURLUSESSL_TRY)
896
29
        result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
897
14
      else if(li->sasl.prefmech != SASL_AUTH_NONE)
898
5
        result = oldap_perform_mechs(data);
899
9
      else
900
9
        result = oldap_perform_bind(data, OLDAP_BIND);
901
43
      break;
902
43
    }
903
1
    result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
904
1
    if(result)
905
0
      break;
906
1
    FALLTHROUGH();
907
1
  case OLDAP_TLS:
908
1
    result = oldap_ssl_connect(data, OLDAP_TLS);
909
1
    if(result)
910
1
      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
1
    break;
923
0
#endif
924
925
319
  case OLDAP_MECHS:
926
319
    result = oldap_state_mechs_resp(data, msg, code);
927
319
    break;
928
0
  case OLDAP_SASL:
929
0
    result = oldap_state_sasl_resp(data, msg, code);
930
0
    break;
931
1.62k
  case OLDAP_BIND:
932
1.63k
  case OLDAP_BINDV2:
933
1.63k
    result = oldap_state_bind_resp(data, msg, code);
934
1.63k
    break;
935
0
  default:
936
    /* internal error */
937
0
    result = CURLE_COULDNT_CONNECT;
938
0
    break;
939
1.99k
  }
940
941
1.99k
  ldap_msgfree(msg);
942
943
1.99k
  *done = li->state == OLDAP_STOP;
944
1.99k
  if(*done)
945
1.23k
    conn->recv[FIRSTSOCKET] = oldap_recv;
946
947
1.99k
  if(result && li->msgid) {
948
10
    ldap_abandon_ext(li->ld, li->msgid, NULL, NULL);
949
10
    li->msgid = 0;
950
10
  }
951
1.99k
  return result;
952
1.99k
}
953
954
static CURLcode oldap_disconnect(struct Curl_easy *data,
955
                                 struct connectdata *conn,
956
                                 bool dead_connection)
957
7.35k
{
958
7.35k
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
959
7.35k
  (void)dead_connection;
960
#ifndef USE_SSL
961
  (void)data;
962
#endif
963
964
7.35k
  if(li && li->ld) {
965
3.20k
#ifdef USE_SSL
966
3.20k
    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
3.20k
#endif
973
3.20k
    ldap_unbind_ext(li->ld, NULL, NULL);
974
3.20k
    li->ld = NULL;
975
3.20k
  }
976
7.35k
  return CURLE_OK;
977
7.35k
}
978
979
static CURLcode oldap_do(struct Curl_easy *data, bool *done)
980
1.23k
{
981
1.23k
  struct connectdata *conn = data->conn;
982
1.23k
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
983
1.23k
  struct ldapreqinfo *lr;
984
1.23k
  CURLcode result;
985
1.23k
  int rc;
986
1.23k
  LDAPURLDesc *lud;
987
1.23k
  int msgid;
988
989
1.23k
  if(!li)
990
0
    return CURLE_FAILED_INIT;
991
992
1.23k
  infof(data, "LDAP local: %s", Curl_bufref_ptr(&data->state.url));
993
994
1.23k
  result = oldap_url_parse(data, &lud);
995
1.23k
  if(result)
996
0
    goto out;
997
998
1.23k
#ifdef USE_SSL
999
1.23k
  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
1.23k
#endif
1009
1010
1.23k
  rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
1011
1.23k
                       lud->lud_filter, lud->lud_attrs, 0,
1012
1.23k
                       NULL, NULL, NULL, 0, &msgid);
1013
1.23k
  ldap_free_urldesc(lud);
1014
1.23k
  if(rc != LDAP_SUCCESS) {
1015
360
    failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
1016
360
    result = CURLE_LDAP_SEARCH_FAILED;
1017
360
    goto out;
1018
360
  }
1019
1020
870
  lr = curlx_calloc(1, sizeof(struct ldapreqinfo));
1021
870
  if(!lr ||
1022
870
     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
870
  lr->msgid = msgid;
1029
870
  Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
1030
870
  *done = TRUE;
1031
1032
1.23k
out:
1033
1.23k
  return result;
1034
870
}
1035
1036
static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
1037
                           bool premature)
1038
3.22k
{
1039
3.22k
  struct connectdata *conn = data->conn;
1040
3.22k
  struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY);
1041
1042
3.22k
  (void)res;
1043
3.22k
  (void)premature;
1044
1045
3.22k
  if(lr) {
1046
    /* if there was a search in progress, abandon it */
1047
870
    if(lr->msgid) {
1048
750
      struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
1049
750
      if(li && li->ld) {
1050
750
        ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
1051
750
      }
1052
750
      lr->msgid = 0;
1053
750
    }
1054
870
    Curl_meta_remove(data, CURL_META_LDAP_EASY);
1055
870
  }
1056
1057
3.22k
  return CURLE_OK;
1058
3.22k
}
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
3.29k
{
1065
3.29k
  CURLcode result = CURLE_OK;
1066
1067
3.29k
  if(prefix) {
1068
    /* If we have a zero-length value and the prefix ends with a space
1069
       separator, drop the latter. */
1070
3.29k
    if(!len && plen && prefix[plen - 1] == ' ')
1071
943
      plen--;
1072
3.29k
    result = Curl_client_write(data, CLIENTWRITE_BODY, prefix, plen);
1073
3.29k
  }
1074
3.29k
  if(!result && value) {
1075
2.74k
    result = Curl_client_write(data, CLIENTWRITE_BODY, value, len);
1076
2.74k
  }
1077
3.29k
  if(!result && suffix) {
1078
2.75k
    result = Curl_client_write(data, CLIENTWRITE_BODY, suffix, slen);
1079
2.75k
  }
1080
3.29k
  return result;
1081
3.29k
}
1082
1083
static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
1084
                           size_t len, size_t *pnread)
1085
1.69k
{
1086
1.69k
  struct connectdata *conn = data->conn;
1087
1.69k
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
1088
1.69k
  struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY);
1089
1.69k
  int rc;
1090
1.69k
  LDAPMessage *msg = NULL;
1091
1.69k
  BerElement *ber = NULL;
1092
1.69k
  struct timeval tv = { 0, 0 };
1093
1.69k
  struct berval bv, *bvals;
1094
1.69k
  CURLcode result = CURLE_AGAIN;
1095
1.69k
  int code;
1096
1.69k
  char *info = NULL;
1097
1098
1.69k
  (void)len;
1099
1.69k
  (void)buf;
1100
1.69k
  (void)sockindex;
1101
1.69k
  *pnread = 0;
1102
1.69k
  if(!li || !lr)
1103
0
    return CURLE_FAILED_INIT;
1104
1105
1.69k
  rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
1106
1.69k
  if(rc < 0) {
1107
677
    failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
1108
677
    result = CURLE_RECV_ERROR;
1109
677
  }
1110
1111
  /* error or timed out */
1112
1.69k
  if(!msg)
1113
1.16k
    return result;
1114
1115
527
  result = CURLE_OK;
1116
1117
527
  switch(ldap_msgtype(msg)) {
1118
120
  case LDAP_RES_SEARCH_RESULT:
1119
120
    lr->msgid = 0;
1120
120
    rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0);
1121
120
    if(rc) {
1122
3
      failf(data, "LDAP local: search ldap_parse_result %s",
1123
3
            ldap_err2string(rc));
1124
3
      result = CURLE_LDAP_SEARCH_FAILED;
1125
3
      break;
1126
3
    }
1127
1128
    /* store the latest code for later retrieval */
1129
117
    data->info.httpcode = code;
1130
1131
117
    switch(code) {
1132
1
    case LDAP_SIZELIMIT_EXCEEDED:
1133
1
      infof(data, "There are more than %d entries", lr->nument);
1134
1
      FALLTHROUGH();
1135
3
    case LDAP_SUCCESS:
1136
3
      data->req.size = data->req.bytecount;
1137
3
      break;
1138
114
    default:
1139
114
      failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code),
1140
114
            info ? info : "");
1141
114
      result = CURLE_LDAP_SEARCH_FAILED;
1142
114
      break;
1143
117
    }
1144
117
    if(info)
1145
23
      ldap_memfree(info);
1146
117
    break;
1147
372
  case LDAP_RES_SEARCH_ENTRY:
1148
372
    lr->nument++;
1149
372
    rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
1150
372
    if(rc < 0) {
1151
3
      result = CURLE_RECV_ERROR;
1152
3
      break;
1153
3
    }
1154
1155
369
    result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len,
1156
369
                          STRCONST("\n"));
1157
369
    if(result)
1158
4
      break;
1159
1160
365
    for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
1161
862
        rc == LDAP_SUCCESS;
1162
575
        rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
1163
575
      int i;
1164
575
      bool binary;
1165
1166
575
      if(!bv.bv_val)
1167
70
        break;
1168
1169
505
      if(!bvals) {
1170
323
        result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1171
323
                              STRCONST(":\n"));
1172
323
        if(result)
1173
1
          break;
1174
322
        continue;
1175
323
      }
1176
1177
182
      binary = bv.bv_len > 7 &&
1178
48
        curl_strnequal(bv.bv_val + bv.bv_len - 7, ";binary", 7);
1179
1180
1.21k
      for(i = 0; bvals[i].bv_val != NULL; i++) {
1181
1.03k
        bool binval = FALSE;
1182
1183
1.03k
        result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1184
1.03k
                              STRCONST(":"));
1185
1.03k
        if(result)
1186
3
          break;
1187
1188
1.03k
        if(!binary) {
1189
          /* check for leading or trailing whitespace */
1190
1.02k
          if(bvals[i].bv_len &&
1191
374
             (ISBLANK(bvals[i].bv_val[0]) ||
1192
348
              ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1])))
1193
53
            binval = TRUE;
1194
970
          else {
1195
            /* check for unprintable characters */
1196
970
            unsigned int j;
1197
1.36k
            for(j = 0; j < bvals[i].bv_len; j++)
1198
679
              if(!ISPRINT(bvals[i].bv_val[j])) {
1199
281
                binval = TRUE;
1200
281
                break;
1201
281
              }
1202
970
          }
1203
1.02k
        }
1204
1.03k
        if(binary || binval) {
1205
344
          char *val_b64 = NULL;
1206
344
          size_t val_b64_sz = 0;
1207
1208
          /* Binary value, encode to base64. */
1209
344
          if(bvals[i].bv_len)
1210
334
            result = curlx_base64_encode((uint8_t *)bvals[i].bv_val,
1211
334
                                         bvals[i].bv_len,
1212
334
                                         &val_b64, &val_b64_sz);
1213
344
          if(!result)
1214
344
            result = client_write(data, STRCONST(": "), val_b64, val_b64_sz,
1215
344
                                  STRCONST("\n"));
1216
344
          curlx_free(val_b64);
1217
344
        }
1218
689
        else
1219
689
          result = client_write(data, STRCONST(" "),
1220
689
                                bvals[i].bv_val, bvals[i].bv_len,
1221
689
                                STRCONST("\n"));
1222
1.03k
        if(result)
1223
3
          break;
1224
1.03k
      }
1225
1226
182
      ber_memfree(bvals);
1227
182
      bvals = NULL;
1228
182
      if(!result)
1229
176
        result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1230
182
      if(result)
1231
7
        break;
1232
182
    }
1233
1234
365
    if(!result)
1235
357
      result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1236
365
    if(!result)
1237
355
      result = CURLE_AGAIN;
1238
365
    break;
1239
527
  }
1240
1241
527
  ber_free(ber, 0);
1242
527
  ldap_msgfree(msg);
1243
527
  return result;
1244
527
}
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 */