Coverage Report

Created: 2026-10-15 06:20

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