Coverage Report

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