Greenbone Vulnerability Management Libraries 22.32.0
ldaputils.c
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2012-2023 Greenbone AG
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later
4 */
5
10
11#include "ldaputils.h"
12
13#ifdef ENABLE_LDAP_AUTH
14
15#include <glib.h> /* for g_free, gchar, g_warning, g_strdup */
16#include <glib/gstdio.h> /* for g_unlink, g_chmod */
17#include <lber.h> /* for berval */
18#include <ldap.h> /* for ldap_err2string, LDAP_SUCCESS, ldap_initialize */
19#include <stdio.h>
20#include <string.h> /* for strlen, strchr, strstr */
21#include <unistd.h> /* for close */
22
23#undef G_LOG_DOMAIN
27#define G_LOG_DOMAIN "libgvm util"
28
29#define KEY_LDAP_HOST "ldaphost"
30#define KEY_LDAP_DN_AUTH "authdn"
31
38
42static void
43ldap_log (const char *message)
44{
45 g_debug ("OpenLDAP: %s", message);
46}
47
53int
55{
56 int ret;
57 static int debug_level = 65535;
58
59#pragma GCC diagnostic push
60#pragma GCC diagnostic ignored "-Wpedantic"
61 // although casting to object pointer is undefined it usually works.
62 // since this method is not defined by us it is ignored.
63 ret = ber_set_option (NULL, LBER_OPT_LOG_PRINT_FN, (void *) ldap_log);
64#pragma GCC diagnostic pop
65 if (ret != LBER_OPT_SUCCESS)
66 {
67 g_warning ("%s: Failed to set LDAP debug print function: %s", __func__,
68 ldap_err2string (ret));
69 return -1;
70 }
71
72 ret = ldap_set_option (NULL, LDAP_OPT_DEBUG_LEVEL, &debug_level);
73 if (ret != LDAP_OPT_SUCCESS)
74 {
75 g_warning ("%s: Failed to set LDAP debug level: %s", __func__,
76 ldap_err2string (ret));
77 return -1;
78 }
79
80 return 0;
81}
82
93int
95 const gchar *username, const gchar *password,
96 /*const */ /*ldap_auth_info_t */ void *ldap_auth_info, const gchar *cacert)
97{
99 LDAP *ldap = NULL;
100 gchar *dn = NULL;
101
102 if (info == NULL || username == NULL || password == NULL || !info->ldap_host)
103 {
104 g_debug ("Not attempting ldap_connect: missing parameter.");
105 return -1;
106 }
107
108 dn = ldap_auth_info_auth_dn (info, username);
109
110 ldap = ldap_auth_bind_2 (info->ldap_host, dn, password,
111 !info->allow_plaintext, cacert, info->ldaps_only);
112
113 if (ldap == NULL)
114 {
115 g_debug ("Could not bind to ldap host %s", info->ldap_host);
116 return -1;
117 }
118
119 ldap_unbind_ext_s (ldap, NULL, NULL);
120
121 return 0;
122}
123
139ldap_auth_info_new (const gchar *ldap_host, const gchar *auth_dn,
140 gboolean allow_plaintext)
141{
142 return ldap_auth_info_new_2 (ldap_host, auth_dn, allow_plaintext, FALSE);
143}
144
161ldap_auth_info_new_2 (const gchar *ldap_host, const gchar *auth_dn,
162 gboolean allow_plaintext, gboolean ldaps_only)
163{
164 // Certain parameters might not be NULL.
165 if (!ldap_host || !auth_dn)
166 return NULL;
167
168 if (ldap_auth_dn_is_good (auth_dn) == FALSE)
169 return NULL;
170
171 ldap_auth_info_t info = g_malloc0 (sizeof (struct ldap_auth_info));
172 info->ldap_host = g_strdup (ldap_host);
173 info->auth_dn = g_strdup (auth_dn);
174 info->allow_plaintext = allow_plaintext;
175 info->ldaps_only = ldaps_only;
176
177 return info;
178}
179
185void
187{
188 if (!info)
189 return;
190
191 g_free (info->ldap_host);
192 g_free (info->auth_dn);
193
194 g_free (info);
195}
196
206gchar *
207ldap_auth_info_auth_dn (const ldap_auth_info_t info, const gchar *username)
208{
209 if (info == NULL || username == NULL)
210 return NULL;
211
212 gchar *dn = g_strdup_printf (info->auth_dn, username);
213
214 return dn;
215}
216
230LDAP *
231ldap_auth_bind (const gchar *host, const gchar *userdn, const gchar *password,
232 gboolean force_encryption, const gchar *cacert)
233{
234 return ldap_auth_bind_2 (host, userdn, password, force_encryption, cacert,
235 FALSE);
236}
237
247static LDAP *
248ldap_init_internal (const char *host, gboolean force_encryption)
249{
250 LDAP *ldap;
251 gchar *ldapuri = NULL;
252 int ldap_return = 0;
253 int ldapv3 = LDAP_VERSION3;
254
255 ldapuri = g_strconcat ("ldap://", host, NULL);
256
257 ldap_return = ldap_initialize (&ldap, ldapuri);
258
259 if (ldap == NULL || ldap_return != LDAP_SUCCESS)
260 {
261 g_warning ("Could not init LDAP connection for authentication.");
262 g_free (ldapuri);
263 return NULL;
264 }
265
266 /* Fail if server doesn't talk LDAPv3 or StartTLS initialization fails. */
267 ldap_return = ldap_set_option (ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapv3);
268 if (ldap_return != LDAP_SUCCESS)
269 {
270 g_warning ("Aborting, could not set ldap protocol version to 3: %s.",
271 ldap_err2string (ldap_return));
272 g_free (ldapuri);
273 return NULL;
274 }
275
276 ldap_return = ldap_start_tls_s (ldap, NULL, NULL);
277 if (ldap_return != LDAP_SUCCESS)
278 {
279 // Try ldaps.
280 g_warning ("StartTLS failed, trying to establish ldaps connection.");
281 g_free (ldapuri);
282 ldapuri = g_strconcat ("ldaps://", host, NULL);
283
284 ldap_return = ldap_initialize (&ldap, ldapuri);
285 if (ldap == NULL || ldap_return != LDAP_SUCCESS)
286 {
287 if (force_encryption == TRUE)
288 {
289 g_warning ("Aborting ldap authentication: Could not init LDAP "
290 "StartTLS nor ldaps: %s.",
291 ldap_err2string (ldap_return));
292 g_free (ldapuri);
293 return NULL;
294 }
295 else
296 {
297 g_warning ("Could not init LDAP StartTLS, nor ldaps: %s.",
298 ldap_err2string (ldap_return));
299 g_warning (
300 "Reinit LDAP connection to do plaintext authentication");
301 ldap_unbind_ext_s (ldap, NULL, NULL);
302
303 // Note that for connections to default ADS, a failed
304 // StartTLS negotiation breaks the future bind, so retry.
305 ldap_return = ldap_initialize (&ldap, ldapuri);
306 if (ldap == NULL || ldap_return != LDAP_SUCCESS)
307 {
308 g_warning (
309 "Could not reopen LDAP connection for authentication.");
310 g_free (ldapuri);
311 return NULL;
312 }
313 // Set LDAP version to 3 after initialization
314 ldap_return =
315 ldap_set_option (ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapv3);
316 if (ldap_return != LDAP_SUCCESS)
317 {
318 g_warning (
319 "Aborting, could not set ldap protocol version to 3: %s.",
320 ldap_err2string (ldap_return));
321 g_free (ldapuri);
322 return NULL;
323 }
324 }
325 }
326 else
327 {
328 // Set LDAP version to 3 after initialization
329 ldap_return =
330 ldap_set_option (ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapv3);
331 if (ldap_return != LDAP_SUCCESS)
332 {
333 g_warning (
334 "Aborting, could not set ldap protocol version to 3: %s.",
335 ldap_err2string (ldap_return));
336 g_free (ldapuri);
337 return NULL;
338 }
339 }
340 }
341 else
342 g_debug ("LDAP StartTLS initialized.");
343
344 g_free (ldapuri);
345
346 return ldap;
347}
348
356static LDAP *
357ldap_init_internal_ldaps_only (const char *host)
358{
359 LDAP *ldap;
360 gchar *ldapuri = NULL;
361 int ldap_return = 0;
362 int ldapv3 = LDAP_VERSION3;
363
364 ldapuri = g_strconcat ("ldaps://", host, NULL);
365
366 ldap_return = ldap_initialize (&ldap, ldapuri);
367 if (ldap == NULL || ldap_return != LDAP_SUCCESS)
368 {
369 g_warning ("Could not init LDAPS connection for authentication.");
370 g_free (ldapuri);
371 return NULL;
372 }
373
374 /* Fail if server doesn't talk LDAPv3. */
375 ldap_return = ldap_set_option (ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapv3);
376 if (ldap_return != LDAP_SUCCESS)
377 {
378 g_warning ("Aborting, could not set ldap protocol version to 3: %s.",
379 ldap_err2string (ldap_return));
380 g_free (ldapuri);
381 return NULL;
382 }
383
384 g_debug ("LDAPS initialized.");
385 g_free (ldapuri);
386 return ldap;
387}
388
403LDAP *
404ldap_auth_bind_2 (const gchar *host, const gchar *userdn, const gchar *password,
405 gboolean force_encryption, const gchar *cacert,
406 gboolean ldaps_only)
407{
408 LDAP *ldap;
409 int ldap_return;
410 struct berval credential;
411 gchar *name;
412 gint fd;
413
414 if (host == NULL || userdn == NULL || password == NULL)
415 return NULL;
416
417 // Prevent empty password, bind against ADS will succeed with
418 // empty password by default.
419 if (strlen (password) == 0)
420 return NULL;
421
422 if (force_encryption == FALSE)
423 g_warning ("Allowed plaintext LDAP authentication.");
424
425 if (cacert)
426 {
427 GError *error;
428
429 error = NULL;
430 fd = g_file_open_tmp (NULL, &name, &error);
431 if (fd == -1)
432 {
433 g_warning ("Could not open temp file for LDAP CACERTFILE: %s",
434 error->message);
435 g_error_free (error);
436 }
437 else
438 {
439 if (g_chmod (name, 0600))
440 g_warning ("Could not chmod for LDAP CACERTFILE");
441
442 g_file_set_contents (name, cacert, strlen (cacert), &error);
443 if (error)
444 {
445 g_warning ("Could not write LDAP CACERTFILE: %s", error->message);
446 g_error_free (error);
447 }
448 else
449 {
450 if (ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE, name)
451 != LDAP_OPT_SUCCESS)
452 g_warning ("Could not set LDAP CACERTFILE option.");
453 }
454 }
455 }
456 else
457 fd = -1;
458
459 if (ldaps_only)
460 ldap = ldap_init_internal_ldaps_only (host);
461 else
462 ldap = ldap_init_internal (host, force_encryption);
463
464 if (ldap == NULL)
465 goto fail;
466
467 int do_search = 0;
468 LDAPDN dn = NULL;
469 gchar *use_dn = NULL;
470 gchar **uid = NULL;
471
472 /* Validate the DN with the LDAP library. */
473 if (ldap_str2dn (userdn, &dn, LDAP_DN_FORMAT_LDAPV3) == LDAP_SUCCESS)
474 {
475 gchar **use_uid = NULL;
476 ldap_memfree (dn);
477 dn = NULL;
478 uid = g_strsplit (userdn, ",", 2);
479 use_uid = g_strsplit (uid[0], "=", 2);
480
481 if (!g_strcmp0 (use_uid[0], "uid"))
482 do_search = 1;
483 else
484 {
485 g_strfreev (uid);
486 uid = NULL;
487 }
488 g_strfreev (use_uid);
489 use_uid = NULL;
490 }
491
492 /* The uid attribute was given, so a search is performed. */
493 if (do_search)
494 {
495 /* Perform anonymous bind to search. */
496 credential.bv_val = NULL;
497 credential.bv_len = 0U;
498 ldap_return = ldap_sasl_bind_s (ldap, NULL, LDAP_SASL_SIMPLE, &credential,
499 NULL, NULL, NULL);
500 if (ldap_return != LDAP_SUCCESS)
501 {
502 g_warning ("LDAP anonymous authentication failure: %s",
503 ldap_err2string (ldap_return));
504 goto fail;
505 }
506 else
507 {
508 char *attrs[2] = {"dn", NULL};
509 LDAPMessage *result = NULL;
510 gchar **base = g_strsplit (userdn, ",", 2);
511
512 /* search for the DN and unbind */
513 ldap_return =
514 ldap_search_ext_s (ldap, base[1], LDAP_SCOPE_SUBTREE, uid[0], attrs,
515 0, NULL, NULL, NULL, 1, &result);
516 g_strfreev (base);
517 base = NULL;
518 g_strfreev (uid);
519 uid = NULL;
520 if (ldap_return != LDAP_SUCCESS)
521 use_dn = g_strdup (userdn);
522 else
523 {
524 gchar *found_dn;
525 found_dn = ldap_get_dn (ldap, result);
526 if ((found_dn == NULL) || (strlen (found_dn) == 0U))
527 use_dn = g_strdup (userdn);
528 else
529 use_dn = g_strdup (found_dn);
530 ldap_memfree (found_dn);
531 }
532 ldap_msgfree (result);
533 }
534 }
535 else
536 use_dn = g_strdup (userdn);
537
538 if (use_dn != NULL)
539 {
540 credential.bv_val = g_strdup (password);
541 credential.bv_len = strlen (password);
542 ldap_return = ldap_sasl_bind_s (ldap, use_dn, LDAP_SASL_SIMPLE,
543 &credential, NULL, NULL, NULL);
544 g_free (credential.bv_val);
545 g_free (use_dn);
546 if (ldap_return != LDAP_SUCCESS)
547 {
548 g_warning ("LDAP authentication failure: %s.",
549 ldap_err2string (ldap_return));
550 goto fail;
551 }
552
553 if (fd > -1)
554 {
555 g_unlink (name);
556 close (fd);
557 g_free (name);
558 }
559 return ldap;
560 }
561
562fail:
563 if (fd > -1)
564 {
565 g_unlink (name);
566 close (fd);
567 g_free (name);
568 }
569 return NULL;
570}
571
579gboolean
580ldap_auth_dn_is_good (const gchar *authdn)
581{
582 gchar *eg;
583 LDAPDN dn;
584 int ln = 0;
585
586 if (authdn == NULL || authdn[0] == '\0')
587 return FALSE;
588
589 // Must contain %s
590 if (!strstr (authdn, "%s"))
591 return FALSE;
592
593 // Must not contain other %-signs
594 char *pos = strchr (authdn, '%');
595 pos = strchr (pos + 1, '%');
596 if (pos != NULL)
597 return FALSE;
598
599 ln = strlen (authdn);
600
601 // As a special exception allow ADS-style domain\user - pairs.
602 if (strchr (authdn, '\\') && authdn[ln - 2] == '%' && authdn[ln - 1] == 's')
603 return TRUE;
604
605 // Also allow user@domain - pairs.
606 if (authdn[0] == '%' && authdn[1] == 's' && authdn[2] == '@')
607 return TRUE;
608
609 /* Validate the DN with the LDAP library. */
610 eg = g_strdup_printf (authdn, "example");
611 dn = NULL;
612 if (ldap_str2dn (eg, &dn, LDAP_DN_FORMAT_LDAPV3))
613 {
614 g_free (eg);
615 return FALSE;
616 }
617 g_free (eg);
618 ldap_memfree (dn);
619
620 return TRUE;
621}
622
623#else
624
630int
632{
633 g_warning ("%s: GVM-libs compiled without LDAP", __func__);
634 return -1;
635}
636
651ldap_auth_info_new (const gchar *ldap_host, const gchar *auth_dn,
652 gboolean allow_plaintext)
653{
654 (void) ldap_host;
655 (void) auth_dn;
656 (void) allow_plaintext;
657 return NULL;
658}
659
675ldap_auth_info_new_2 (const gchar *ldap_host, const gchar *auth_dn,
676 gboolean allow_plaintext, gboolean ldaps_only)
677{
678 (void) ldap_host;
679 (void) auth_dn;
680 (void) allow_plaintext;
681 (void) ldaps_only;
682 return NULL;
683}
684
695int
697 const gchar *username, const gchar *password,
698 /*const */ /*ldap_auth_info_t */ void *ldap_auth_info, const gchar *cacert)
699{
700 (void) username;
701 (void) password;
702 (void) ldap_auth_info;
703 (void) cacert;
704 return -1;
705}
706
712void
714{
715 (void) info;
716}
717
718#endif /* ENABLE_LDAP_AUTH */
int ldap_enable_debug()
Dummy function for enabling LDAP debugging for manager.
Definition ldaputils.c:631
int ldap_connect_authenticate(const gchar *username, const gchar *password, void *ldap_auth_info, const gchar *cacert)
Dummy function for Manager.
Definition ldaputils.c:696
void ldap_auth_info_free(ldap_auth_info_t info)
Dummy function for Manager.
Definition ldaputils.c:713
ldap_auth_info_t ldap_auth_info_new(const gchar *ldap_host, const gchar *auth_dn, gboolean allow_plaintext)
Dummy function for manager.
Definition ldaputils.c:651
ldap_auth_info_t ldap_auth_info_new_2(const gchar *ldap_host, const gchar *auth_dn, gboolean allow_plaintext, gboolean ldaps_only)
Dummy function for manager.
Definition ldaputils.c:675
Header for LDAP-Connect Authentication module.
struct ldap_auth_info * ldap_auth_info_t
Authentication schema and address type.
Definition ldaputils.h:17
Schema (dn) and info to use for a basic ldap authentication.
Definition ldaputils.h:26
gboolean allow_plaintext
!Whether or not StartTLS or LDAPS is required.
Definition ldaputils.h:29
gboolean ldaps_only
Whether to try LDAPS before StartTLS.
Definition ldaputils.h:30
gchar * ldap_host
Address of the ldap server, might include port.
Definition ldaputils.h:27
gchar * auth_dn
DN to authenticate with.
Definition ldaputils.h:28