Greenbone Vulnerability Management Libraries  22.8.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 
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
24 
27 #define G_LOG_DOMAIN "libgvm util"
28 
29 #define KEY_LDAP_HOST "ldaphost"
30 #define KEY_LDAP_DN_AUTH "authdn"
31 
42 static void
43 ldap_log (const char *message)
44 {
45  g_debug ("OpenLDAP: %s", message);
46 }
47 
53 int
54 ldap_enable_debug (void)
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 
93 int
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 
139 ldap_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 
161 ldap_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 
185 void
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 
206 gchar *
207 ldap_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 
230 LDAP *
231 ldap_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 
247 static LDAP *
248 ldap_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 
356 static LDAP *
357 ldap_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 
403 LDAP *
404 ldap_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 
562 fail:
563  if (fd > -1)
564  {
565  g_unlink (name);
566  close (fd);
567  g_free (name);
568  }
569  return NULL;
570 }
571 
579 gboolean
580 ldap_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 
630 int
632 {
633  g_warning ("%s: GVM-libs compiled without LDAP", __func__);
634  return -1;
635 }
636 
651 ldap_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 
675 ldap_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 
695 int
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 
712 void
714 {
715  (void) info;
716 }
717 
718 #endif /* ENABLE_LDAP_AUTH */
ldap_connect_authenticate
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
ldap_auth_info::ldap_host
gchar * ldap_host
Address of the ldap server, might include port.
Definition: ldaputils.h:27
ldap_auth_info::auth_dn
gchar * auth_dn
DN to authenticate with.
Definition: ldaputils.h:28
ldap_enable_debug
int ldap_enable_debug()
Dummy function for enabling LDAP debugging for manager.
Definition: ldaputils.c:631
ldap_auth_info::allow_plaintext
gboolean allow_plaintext
!Whether or not StartTLS or LDAPS is required.
Definition: ldaputils.h:29
ldap_auth_info
Schema (dn) and info to use for a basic ldap authentication.
Definition: ldaputils.h:26
ldaputils.h
Header for LDAP-Connect Authentication module.
ldap_auth_info_new_2
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
ldap_auth_info_t
struct ldap_auth_info * ldap_auth_info_t
Authentication schema and address type.
Definition: ldaputils.h:17
ldap_auth_info_free
void ldap_auth_info_free(ldap_auth_info_t info)
Dummy function for Manager.
Definition: ldaputils.c:713
ldap_auth_info_new
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::ldaps_only
gboolean ldaps_only
Whether to try LDAPS before StartTLS.
Definition: ldaputils.h:30