Coverage Report

Created: 2026-01-09 07:25

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