Coverage Report

Created: 2026-10-15 06:18

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
0
#define CURL_META_LDAP_EASY   "meta:proto:ldap:easy"
115
/* meta key for storing ldapconninfo at connection */
116
0
#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
0
{
126
0
#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
127
  /* for debug purposes */
128
0
  static const char * const names[] = {
129
0
    "STOP",
130
0
    "SSL",
131
0
    "STARTTLS",
132
0
    "TLS",
133
0
    "MECHS",
134
0
    "SASL",
135
0
    "BIND",
136
0
    "BINDV2",
137
    /* LAST */
138
0
  };
139
140
0
  if(li->state != newstate)
141
0
    infof(data, "LDAP %p state change from %s to %s",
142
0
          (void *)li, names[li->state], names[newstate]);
143
#else
144
  (void)data;
145
#endif
146
0
  li->state = newstate;
147
0
}
148
149
/* Map some particular LDAP error codes to CURLcode values. */
150
static CURLcode oldap_map_error(int rc, CURLcode result)
151
0
{
152
0
  switch(rc) {
153
0
  case LDAP_NO_MEMORY:
154
0
    return CURLE_OUT_OF_MEMORY;
155
0
  case LDAP_INVALID_CREDENTIALS:
156
0
    return CURLE_LOGIN_DENIED;
157
0
  case LDAP_PROTOCOL_ERROR:
158
0
    return CURLE_UNSUPPORTED_PROTOCOL;
159
0
  case LDAP_INSUFFICIENT_ACCESS:
160
0
    return CURLE_REMOTE_ACCESS_DENIED;
161
0
  }
162
0
  return result;
163
0
}
164
165
static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
166
0
{
167
0
  CURLcode result = CURLE_OK;
168
0
  int rc;
169
0
  static const char * const url_errs[] = {
170
0
    "success",
171
0
    "out of memory",
172
0
    "bad parameter",
173
0
    "unrecognized scheme",
174
0
    "unbalanced delimiter",
175
0
    "bad URL",
176
0
    "bad host or port",
177
0
    "bad or missing attributes",
178
0
    "bad or missing scope",
179
0
    "bad or missing filter",
180
0
    "bad or missing extensions"
181
0
  };
182
183
0
  *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
0
  if((data->state.creds && (data->state.creds->source == CREDS_URL)) ||
189
0
     data->state.up.options)
190
0
    rc = LDAP_URL_ERR_BADURL;
191
0
  else
192
0
    rc = ldap_url_parse(Curl_bufref_ptr(&data->state.url), ludp);
193
194
0
  if(rc != LDAP_URL_SUCCESS) {
195
0
    const char *msg = "url parsing problem";
196
197
0
    result = rc == LDAP_URL_ERR_MEM ? CURLE_OUT_OF_MEMORY :
198
0
      CURLE_URL_MALFORMAT;
199
0
    rc -= LDAP_URL_SUCCESS;
200
0
    if((size_t)rc < CURL_ARRAYSIZE(url_errs))
201
0
      msg = url_errs[rc];
202
0
    failf(data, "LDAP local: %s", msg);
203
0
  }
204
0
  return result;
205
0
}
206
207
/* Parse the login options. */
208
static CURLcode oldap_parse_login_options(struct connectdata *conn)
209
0
{
210
0
  CURLcode result = CURLE_OK;
211
0
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
212
0
  const char *ptr = conn->options;
213
214
0
  DEBUGASSERT(li);
215
0
  if(!li)
216
0
    return CURLE_FAILED_INIT;
217
218
0
  while(!result && ptr && *ptr) {
219
0
    const char *key = ptr;
220
0
    const char *value;
221
222
0
    while(*ptr && *ptr != '=')
223
0
      ptr++;
224
225
0
    value = ptr + 1;
226
227
0
    while(*ptr && *ptr != ';')
228
0
      ptr++;
229
230
0
    if(checkprefix("AUTH=", key))
231
0
      result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value);
232
0
    else
233
0
      result = CURLE_SETOPT_OPTION_SYNTAX;
234
235
0
    if(*ptr == ';')
236
0
      ptr++;
237
0
  }
238
239
0
  return result == CURLE_URL_MALFORMAT ? CURLE_SETOPT_OPTION_SYNTAX : result;
240
0
}
241
242
static CURLcode oldap_setup_connection(struct Curl_easy *data,
243
                                       struct connectdata *conn)
244
0
{
245
0
  CURLcode result;
246
0
  LDAPURLDesc *lud;
247
0
  (void)conn;
248
249
  /* Early URL syntax check. */
250
0
  result = oldap_url_parse(data, &lud);
251
0
  ldap_free_urldesc(lud);
252
253
0
  return result;
254
0
}
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
0
{
345
0
  struct connectdata *conn = data->conn;
346
0
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
347
0
  const char *binddn = NULL;
348
0
  struct berval passwd;
349
0
  int rc;
350
351
0
  if(!li)
352
0
    return CURLE_FAILED_INIT;
353
0
  passwd.bv_val = NULL;
354
0
  passwd.bv_len = 0;
355
356
0
  if(conn->creds) {
357
0
    binddn = Curl_creds_user(conn->creds);
358
0
    passwd.bv_val = CURL_UNCONST(Curl_creds_passwd(conn->creds));
359
0
    passwd.bv_len = strlen(passwd.bv_val);
360
0
  }
361
362
0
  rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
363
0
                      NULL, NULL, &li->msgid);
364
0
  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
0
  oldap_state(data, li, newstate);
369
0
  return CURLE_OK;
370
0
}
371
372
/* Query the supported SASL authentication mechanisms. */
373
static CURLcode oldap_perform_mechs(struct Curl_easy *data)
374
0
{
375
0
  struct ldapconninfo *li =
376
0
    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
377
0
  int rc;
378
0
  static const char * const supportedSASLMechanisms[] = {
379
0
    "supportedSASLMechanisms",
380
0
    NULL
381
0
  };
382
383
0
  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
0
  rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
390
0
                       (char **)CURL_UNCONST(supportedSASLMechanisms), 0,
391
0
                       NULL, NULL, NULL, 0, &li->msgid);
392
0
  if(rc != LDAP_SUCCESS)
393
0
    return oldap_map_error(rc, CURLE_LOGIN_DENIED);
394
0
  oldap_state(data, li, OLDAP_MECHS);
395
0
  return CURLE_OK;
396
0
}
397
398
/* Starts SASL bind. */
399
static CURLcode oldap_perform_sasl(struct Curl_easy *data)
400
0
{
401
0
  struct ldapconninfo *li =
402
0
    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
403
0
  saslprogress progress = SASL_IDLE;
404
0
  CURLcode result;
405
406
0
  if(!li)
407
0
    return CURLE_FAILED_INIT;
408
0
  result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
409
410
0
  oldap_state(data, li, OLDAP_SASL);
411
0
  if(!result && progress != SASL_INPROGRESS)
412
0
    result = Curl_sasl_is_blocked(&li->sasl, data);
413
0
  return result;
414
0
}
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
0
{
520
0
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
521
0
  return li && li->recv;
522
0
}
523
524
static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
525
0
{
526
0
  struct connectdata *conn = data->conn;
527
0
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
528
0
  bool ssldone = FALSE;
529
0
  CURLcode result;
530
531
0
  if(!li)
532
0
    return CURLE_FAILED_INIT;
533
0
  result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
534
0
  if(result)
535
0
    return result;
536
0
  oldap_state(data, li, newstate);
537
538
0
  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
0
  return result;
550
0
}
551
552
/* Send the STARTTLS request */
553
static CURLcode oldap_perform_starttls(struct Curl_easy *data)
554
0
{
555
0
  struct ldapconninfo *li =
556
0
    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
557
0
  int rc;
558
559
0
  if(!li)
560
0
    return CURLE_FAILED_INIT;
561
0
  rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
562
0
  if(rc != LDAP_SUCCESS)
563
0
    return oldap_map_error(rc, CURLE_USE_SSL_FAILED);
564
0
  oldap_state(data, li, OLDAP_STARTTLS);
565
0
  return CURLE_OK;
566
0
}
567
#endif
568
569
static void oldap_easy_dtor(const void *key, size_t klen, void *entry)
570
0
{
571
0
  struct ldapreqinfo *lr = entry;
572
0
  (void)key;
573
0
  (void)klen;
574
0
  curlx_free(lr);
575
0
}
576
577
static void oldap_conn_dtor(const void *key, size_t klen, void *entry)
578
0
{
579
0
  struct ldapconninfo *li = entry;
580
0
  (void)key;
581
0
  (void)klen;
582
0
  if(li->ld) {
583
0
    ldap_unbind_ext(li->ld, NULL, NULL);
584
0
    li->ld = NULL;
585
0
  }
586
0
  curlx_free(li);
587
0
}
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
0
{
605
0
  struct connectdata *conn = data->conn;
606
0
  struct ldapconninfo *li;
607
0
  static const int version = LDAP_VERSION3;
608
0
  char *hosturl = NULL;
609
0
  CURLcode result;
610
0
  int rc;
611
#ifdef CURL_OPENLDAP_DEBUG
612
  static int do_trace = -1;
613
#endif
614
615
0
  (void)done;
616
617
0
  li = curlx_calloc(1, sizeof(struct ldapconninfo));
618
0
  if(!li) {
619
0
    result = CURLE_OUT_OF_MEMORY;
620
0
    goto out;
621
0
  }
622
0
  li->dupfd = CURL_SOCKET_BAD;
623
624
0
  result = Curl_conn_meta_set(conn, CURL_META_LDAP_CONN, li, oldap_conn_dtor);
625
0
  if(result)
626
0
    goto out;
627
628
0
  li->proto = ldap_pvt_url_scheme2proto(data->state.origin->scheme->name);
629
630
  /* Initialize the SASL storage */
631
0
  Curl_sasl_init(&li->sasl, data, &saslldap);
632
633
0
  result = oldap_parse_login_options(conn);
634
0
  if(result)
635
0
    goto out;
636
637
0
  hosturl = curl_maprintf("%s://%s:%u",
638
0
                          conn->scheme->name,
639
0
                          conn->origin->ipv6 ?
640
0
                          conn->origin->user_hostname :
641
0
                          conn->origin->hostname,
642
0
                          conn->origin->port);
643
0
  if(!hosturl) {
644
0
    result = CURLE_OUT_OF_MEMORY;
645
0
    goto out;
646
0
  }
647
648
0
  {
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
0
#ifdef F_DUPFD_CLOEXEC
659
0
    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
0
#endif /* _WIN32 */
671
672
0
    if(dupfd == CURL_SOCKET_BAD) {
673
0
      result = CURLE_COULDNT_CONNECT;
674
0
      goto out;
675
0
    }
676
0
    rc = ldap_init_fd((ber_socket_t)dupfd, li->proto, hosturl, &li->ld);
677
0
    if(rc)
678
0
      sclose(dupfd);
679
0
    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
0
      li->dupfd = dupfd;
684
0
  }
685
0
  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
0
  ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
705
706
  /* Do not chase referrals. */
707
0
  ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
708
709
0
  {
710
0
    ber_len_t max = 256 * 1024;
711
0
    Sockbuf *sb;
712
0
    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
0
       !ber_sockbuf_ctrl(sb, LBER_SB_OPT_SET_MAX_INCOMING, &max)) {
717
0
      result = CURLE_FAILED_INIT;
718
0
      goto out;
719
0
    }
720
0
  }
721
722
0
#ifdef USE_SSL
723
0
  if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
724
0
    result = oldap_ssl_connect(data, OLDAP_SSL);
725
0
    goto out;
726
0
  }
727
728
0
  if(data->set.use_ssl) {
729
0
    result = oldap_perform_starttls(data);
730
0
    if(!result || data->set.use_ssl != CURLUSESSL_TRY)
731
0
      goto out;
732
0
  }
733
0
#endif
734
735
0
  if(li->sasl.prefmech != SASL_AUTH_NONE) {
736
0
    result = oldap_perform_mechs(data);
737
0
    goto out;
738
0
  }
739
740
  /* Force bind even if anonymous bind is not needed in protocol version 3
741
     to detect missing version 3 support. */
742
0
  result = oldap_perform_bind(data, OLDAP_BIND);
743
744
0
out:
745
0
  curlx_free(hosturl);
746
0
  return result;
747
0
}
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
0
{
753
0
  struct connectdata *conn;
754
0
  struct ldapconninfo *li;
755
0
  int rc;
756
0
  BerElement *ber = NULL;
757
0
  CURLcode result = CURLE_OK;
758
0
  struct berval bv, *bvals;
759
760
0
  if(!data)
761
0
    return CURLE_FAILED_INIT;
762
763
0
  conn = data->conn;
764
0
  li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
765
766
0
  if(!li)
767
0
    return CURLE_FAILED_INIT;
768
0
  switch(ldap_msgtype(msg)) {
769
0
  case LDAP_RES_SEARCH_ENTRY:
770
    /* Got a list of supported SASL mechanisms. */
771
0
    if(code != LDAP_SUCCESS && code != LDAP_NO_RESULTS_RETURNED)
772
0
      return CURLE_LOGIN_DENIED;
773
774
0
    rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
775
0
    if(rc < 0)
776
0
      return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING);
777
0
    for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
778
0
        rc == LDAP_SUCCESS;
779
0
        rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
780
0
      int i;
781
782
0
      if(!bv.bv_val)
783
0
        break;
784
785
0
      if(bvals) {
786
0
        for(i = 0; bvals[i].bv_val; i++) {
787
0
          size_t llen;
788
0
          unsigned short mech =
789
0
            Curl_sasl_decode_mech((const char *)bvals[i].bv_val,
790
0
                                  bvals[i].bv_len, &llen);
791
0
          if(bvals[i].bv_len == llen)
792
0
            li->sasl.authmechs |= mech;
793
0
        }
794
0
        ber_memfree(bvals);
795
0
      }
796
0
    }
797
0
    ber_free(ber, 0);
798
0
    break;
799
800
0
  case LDAP_RES_SEARCH_RESULT:
801
0
    switch(code) {
802
0
    case LDAP_SIZELIMIT_EXCEEDED:
803
0
      infof(data, "Too many authentication mechanisms");
804
0
      FALLTHROUGH();
805
0
    case LDAP_SUCCESS:
806
0
    case LDAP_NO_RESULTS_RETURNED:
807
0
      if(Curl_sasl_can_authenticate(&li->sasl, data))
808
0
        result = oldap_perform_sasl(data);
809
0
      else
810
0
        result = CURLE_LOGIN_DENIED;
811
0
      break;
812
0
    default:
813
0
      result = oldap_map_error(code, CURLE_LOGIN_DENIED);
814
0
      break;
815
0
    }
816
0
    break;
817
0
  default:
818
0
    break;
819
0
  }
820
0
  return result;
821
0
}
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
0
{
867
0
  struct connectdata *conn = data->conn;
868
0
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
869
0
  CURLcode result = CURLE_OK;
870
0
  struct berval *bv = NULL;
871
0
  int rc;
872
873
0
  if(!li)
874
0
    return CURLE_FAILED_INIT;
875
876
0
  if(code != LDAP_SUCCESS)
877
0
    return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND);
878
879
0
  rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0);
880
0
  if(rc != LDAP_SUCCESS) {
881
0
    failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s",
882
0
          ldap_err2string(rc));
883
0
    result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
884
0
  }
885
0
  else
886
0
    oldap_state(data, li, OLDAP_STOP);
887
888
0
  if(bv)
889
0
    ber_bvfree(bv);
890
0
  return result;
891
0
}
892
893
static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
894
0
{
895
0
  CURLcode result = CURLE_OK;
896
0
  struct connectdata *conn = data->conn;
897
0
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
898
0
  LDAPMessage *msg = NULL;
899
0
  struct timeval tv = { 0, 0 };
900
0
  int code = LDAP_SUCCESS;
901
0
  int rc;
902
903
0
  if(!li)
904
0
    return CURLE_FAILED_INIT;
905
906
0
  if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) {
907
    /* Get response to last command. */
908
0
    rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg);
909
0
    switch(rc) {
910
0
    case 0:                               /* Timed out. */
911
0
      return CURLE_OK;
912
0
    case LDAP_RES_SEARCH_ENTRY:
913
0
    case LDAP_RES_SEARCH_REFERENCE:
914
0
      break;
915
0
    default:
916
0
      li->msgid = 0;                      /* Nothing to abandon upon error. */
917
0
      if(rc < 0) {
918
0
        failf(data, "LDAP local: connecting ldap_result %s",
919
0
              ldap_err2string(rc));
920
0
        return oldap_map_error(rc, CURLE_COULDNT_CONNECT);
921
0
      }
922
0
      break;
923
0
    }
924
925
    /* Get error code from message. */
926
0
    rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0);
927
0
    if(rc)
928
0
      code = rc;
929
0
    else {
930
      /* store the latest code for later retrieval */
931
0
      data->info.httpcode = code;
932
0
    }
933
934
    /* If protocol version 3 is not supported, fallback to version 2. */
935
0
    if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 &&
936
0
#ifdef USE_SSL
937
0
       (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) &&
938
0
#endif
939
0
       li->sasl.prefmech == SASL_AUTH_NONE) {
940
0
      static const int version = LDAP_VERSION2;
941
942
0
      ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
943
0
      ldap_msgfree(msg);
944
0
      return oldap_perform_bind(data, OLDAP_BINDV2);
945
0
    }
946
0
  }
947
948
  /* Handle response message according to current state. */
949
0
  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
0
  case OLDAP_STARTTLS:
962
0
    if(code != LDAP_SUCCESS) {
963
0
      if(data->set.use_ssl != CURLUSESSL_TRY)
964
0
        result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
965
0
      else if(li->sasl.prefmech != SASL_AUTH_NONE)
966
0
        result = oldap_perform_mechs(data);
967
0
      else
968
0
        result = oldap_perform_bind(data, OLDAP_BIND);
969
0
      break;
970
0
    }
971
0
    result = Curl_ssl_cfilter_add(
972
0
      data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET);
973
0
    if(result)
974
0
      break;
975
0
    FALLTHROUGH();
976
0
  case OLDAP_TLS:
977
0
    result = oldap_ssl_connect(data, OLDAP_TLS);
978
0
    if(result)
979
0
      result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
980
0
    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
0
    break;
992
0
#endif
993
994
0
  case OLDAP_MECHS:
995
0
    result = oldap_state_mechs_resp(data, msg, code);
996
0
    break;
997
0
  case OLDAP_SASL:
998
0
    result = oldap_state_sasl_resp(data, msg, code);
999
0
    break;
1000
0
  case OLDAP_BIND:
1001
0
  case OLDAP_BINDV2:
1002
0
    result = oldap_state_bind_resp(data, msg, code);
1003
0
    break;
1004
0
  default:
1005
    /* internal error */
1006
0
    result = CURLE_COULDNT_CONNECT;
1007
0
    break;
1008
0
  }
1009
1010
0
  ldap_msgfree(msg);
1011
1012
0
  *done = li->state == OLDAP_STOP;
1013
0
  if(*done)
1014
0
    conn->recv[FIRSTSOCKET] = oldap_recv;
1015
1016
0
  if(result && li->msgid) {
1017
0
    ldap_abandon_ext(li->ld, li->msgid, NULL, NULL);
1018
0
    li->msgid = 0;
1019
0
  }
1020
0
  return result;
1021
0
}
1022
1023
static CURLcode oldap_disconnect(struct Curl_easy *data,
1024
                                 struct connectdata *conn,
1025
                                 bool dead_connection)
1026
0
{
1027
0
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
1028
0
  (void)dead_connection;
1029
#ifndef USE_SSL
1030
  (void)data;
1031
#endif
1032
1033
0
  if(li && li->ld) {
1034
0
#ifdef USE_SSL
1035
0
    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
0
#endif
1042
0
    ldap_unbind_ext(li->ld, NULL, NULL);
1043
0
    li->ld = NULL;
1044
0
  }
1045
0
  return CURLE_OK;
1046
0
}
1047
1048
static CURLcode oldap_do(struct Curl_easy *data, bool *done)
1049
0
{
1050
0
  struct connectdata *conn = data->conn;
1051
0
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
1052
0
  struct ldapreqinfo *lr;
1053
0
  CURLcode result;
1054
0
  int rc;
1055
0
  LDAPURLDesc *lud;
1056
0
  int msgid;
1057
1058
0
  if(!li)
1059
0
    return CURLE_FAILED_INIT;
1060
1061
0
  infof(data, "LDAP local: %s", Curl_bufref_ptr(&data->state.url));
1062
1063
0
  result = oldap_url_parse(data, &lud);
1064
0
  if(result)
1065
0
    goto out;
1066
1067
0
#ifdef USE_SSL
1068
0
  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
0
#endif
1078
1079
0
  rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
1080
0
                       lud->lud_filter, lud->lud_attrs, 0,
1081
0
                       NULL, NULL, NULL, 0, &msgid);
1082
0
  ldap_free_urldesc(lud);
1083
0
  if(rc != LDAP_SUCCESS) {
1084
0
    failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
1085
0
    result = CURLE_LDAP_SEARCH_FAILED;
1086
0
    goto out;
1087
0
  }
1088
1089
0
  lr = curlx_calloc(1, sizeof(struct ldapreqinfo));
1090
0
  if(!lr ||
1091
0
     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
0
  lr->msgid = msgid;
1098
0
  Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
1099
0
  *done = TRUE;
1100
1101
0
out:
1102
0
  return result;
1103
0
}
1104
1105
static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
1106
                           bool premature)
1107
0
{
1108
0
  struct connectdata *conn = data->conn;
1109
0
  struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY);
1110
1111
0
  (void)res;
1112
0
  (void)premature;
1113
1114
0
  if(lr) {
1115
    /* if there was a search in progress, abandon it */
1116
0
    if(lr->msgid) {
1117
0
      struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
1118
0
      if(li && li->ld) {
1119
0
        ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
1120
0
      }
1121
0
      lr->msgid = 0;
1122
0
    }
1123
0
    Curl_meta_remove(data, CURL_META_LDAP_EASY);
1124
0
  }
1125
1126
0
  return CURLE_OK;
1127
0
}
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
0
{
1134
0
  CURLcode result = CURLE_OK;
1135
1136
0
  if(prefix) {
1137
    /* If we have a zero-length value and the prefix ends with a space
1138
       separator, drop the latter. */
1139
0
    if(!len && plen && prefix[plen - 1] == ' ')
1140
0
      plen--;
1141
0
    result = Curl_client_write(data, CLIENTWRITE_BODY, prefix, plen);
1142
0
  }
1143
0
  if(!result && value) {
1144
0
    result = Curl_client_write(data, CLIENTWRITE_BODY, value, len);
1145
0
  }
1146
0
  if(!result && suffix) {
1147
0
    result = Curl_client_write(data, CLIENTWRITE_BODY, suffix, slen);
1148
0
  }
1149
0
  return result;
1150
0
}
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
0
{
1158
0
  size_t i;
1159
0
  if(!len)
1160
0
    return FALSE;
1161
0
  if((s[0] == ':') || (s[0] == '<') || ISBLANK(s[0]) || ISBLANK(s[len - 1]))
1162
0
    return TRUE;
1163
0
  for(i = 0; i < len; i++)
1164
0
    if(!ISPRINT(s[i]))
1165
0
      return TRUE;
1166
0
  return FALSE;
1167
0
}
1168
1169
static CURLcode oldap_recv(struct Curl_easy *data, int8_t sockindex, char *buf,
1170
                           size_t len, size_t *pnread)
1171
0
{
1172
0
  struct connectdata *conn = data->conn;
1173
0
  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
1174
0
  struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY);
1175
0
  int rc;
1176
0
  LDAPMessage *msg = NULL;
1177
0
  BerElement *ber = NULL;
1178
0
  struct timeval tv = { 0, 0 };
1179
0
  struct berval bv, *bvals;
1180
0
  CURLcode result = CURLE_AGAIN;
1181
0
  int code;
1182
0
  char *info = NULL;
1183
1184
0
  (void)len;
1185
0
  (void)buf;
1186
0
  (void)sockindex;
1187
0
  *pnread = 0;
1188
0
  if(!li || !lr)
1189
0
    return CURLE_FAILED_INIT;
1190
1191
0
  rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
1192
0
  if(rc < 0) {
1193
0
    failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
1194
0
    result = CURLE_RECV_ERROR;
1195
0
  }
1196
1197
  /* error or timed out */
1198
0
  if(!msg)
1199
0
    return result;
1200
1201
0
  result = CURLE_OK;
1202
1203
0
  switch(ldap_msgtype(msg)) {
1204
0
  case LDAP_RES_SEARCH_RESULT:
1205
0
    lr->msgid = 0;
1206
0
    rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0);
1207
0
    if(rc) {
1208
0
      failf(data, "LDAP local: search ldap_parse_result %s",
1209
0
            ldap_err2string(rc));
1210
0
      result = CURLE_LDAP_SEARCH_FAILED;
1211
0
      break;
1212
0
    }
1213
1214
    /* store the latest code for later retrieval */
1215
0
    data->info.httpcode = code;
1216
1217
0
    switch(code) {
1218
0
    case LDAP_SIZELIMIT_EXCEEDED:
1219
0
      infof(data, "There are more than %d entries", lr->nument);
1220
0
      FALLTHROUGH();
1221
0
    case LDAP_SUCCESS:
1222
0
      data->req.size = data->req.bytecount;
1223
0
      break;
1224
0
    default:
1225
0
      failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code),
1226
0
            info ? info : "");
1227
0
      result = CURLE_LDAP_SEARCH_FAILED;
1228
0
      break;
1229
0
    }
1230
0
    if(info)
1231
0
      ldap_memfree(info);
1232
0
    break;
1233
0
  case LDAP_RES_SEARCH_ENTRY:
1234
0
    lr->nument++;
1235
0
    rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
1236
0
    if(rc < 0) {
1237
0
      result = CURLE_RECV_ERROR;
1238
0
      break;
1239
0
    }
1240
1241
0
    if(oldap_str_needs_base64(bv.bv_val, bv.bv_len)) {
1242
0
      char *dn_b64 = NULL;
1243
0
      size_t dn_b64_sz = 0;
1244
1245
      /* A DN with e.g. CR/LF would inject LDIF lines, so base64-encode it. */
1246
0
      result = curlx_base64_encode((uint8_t *)bv.bv_val, bv.bv_len,
1247
0
                                   &dn_b64, &dn_b64_sz);
1248
0
      if(!result)
1249
0
        result = client_write(data, STRCONST("DN:: "), dn_b64, dn_b64_sz,
1250
0
                              STRCONST("\n"));
1251
0
      curlx_free(dn_b64);
1252
0
    }
1253
0
    else
1254
0
      result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len,
1255
0
                            STRCONST("\n"));
1256
0
    if(result)
1257
0
      break;
1258
1259
0
    for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
1260
0
        rc == LDAP_SUCCESS;
1261
0
        rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
1262
0
      int i;
1263
0
      bool binary;
1264
1265
0
      if(!bv.bv_val)
1266
0
        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
0
      if(oldap_str_needs_base64(bv.bv_val, bv.bv_len)) {
1272
0
        failf(data, "LDAP local: attribute type contains illegal bytes");
1273
0
        if(bvals)
1274
0
          ber_memfree(bvals);
1275
0
        result = CURLE_WEIRD_SERVER_REPLY;
1276
0
        break;
1277
0
      }
1278
1279
0
      if(!bvals) {
1280
0
        result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1281
0
                              STRCONST(":\n"));
1282
0
        if(result)
1283
0
          break;
1284
0
        continue;
1285
0
      }
1286
1287
0
      binary = bv.bv_len > 7 &&
1288
0
        curl_strnequal(bv.bv_val + bv.bv_len - 7, ";binary", 7);
1289
1290
0
      for(i = 0; bvals[i].bv_val; i++) {
1291
0
        bool binval = FALSE;
1292
1293
0
        result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
1294
0
                              STRCONST(":"));
1295
0
        if(result)
1296
0
          break;
1297
1298
0
        if(!binary)
1299
0
          binval = oldap_str_needs_base64(bvals[i].bv_val, bvals[i].bv_len);
1300
0
        if(binary || binval) {
1301
0
          char *val_b64 = NULL;
1302
0
          size_t val_b64_sz = 0;
1303
1304
          /* Binary value, encode to base64. */
1305
0
          if(bvals[i].bv_len)
1306
0
            result = curlx_base64_encode((uint8_t *)bvals[i].bv_val,
1307
0
                                         bvals[i].bv_len,
1308
0
                                         &val_b64, &val_b64_sz);
1309
0
          if(!result)
1310
0
            result = client_write(data, STRCONST(": "), val_b64, val_b64_sz,
1311
0
                                  STRCONST("\n"));
1312
0
          curlx_free(val_b64);
1313
0
        }
1314
0
        else
1315
0
          result = client_write(data, STRCONST(" "),
1316
0
                                bvals[i].bv_val, bvals[i].bv_len,
1317
0
                                STRCONST("\n"));
1318
0
        if(result)
1319
0
          break;
1320
0
      }
1321
1322
0
      ber_memfree(bvals);
1323
0
      bvals = NULL;
1324
0
      if(!result)
1325
0
        result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1326
0
      if(result)
1327
0
        break;
1328
0
    }
1329
1330
0
    if(!result)
1331
0
      result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
1332
0
    if(!result)
1333
0
      result = CURLE_AGAIN;
1334
0
    break;
1335
0
  }
1336
1337
0
  ber_free(ber, 0);
1338
0
  ldap_msgfree(msg);
1339
0
  return result;
1340
0
}
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 */