Greenbone Vulnerability Management Libraries  10.0.0
ldaputils.c
Go to the documentation of this file.
1 /* Copyright (C) 2012-2019 Greenbone Networks GmbH
2  *
3  * SPDX-License-Identifier: GPL-2.0-or-later
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
25 #include "ldaputils.h"
26 
27 #ifdef ENABLE_LDAP_AUTH
28 
29 #include <glib.h> /* for g_free, gchar, g_warning, g_strdup */
30 #include <glib/gstdio.h> /* for g_unlink, g_chmod */
31 #include <lber.h> /* for berval */
32 #include <ldap.h> /* for ldap_err2string, LDAP_SUCCESS, ldap_initialize */
33 #include <stdio.h>
34 #include <string.h> /* for strlen, strchr, strstr */
35 #include <unistd.h> /* for close */
36 
37 #undef G_LOG_DOMAIN
38 
41 #define G_LOG_DOMAIN "lib ldap"
42 
43 #define KEY_LDAP_HOST "ldaphost"
44 #define KEY_LDAP_DN_AUTH "authdn"
45 
63 int
65  const gchar *username, const gchar *password,
66  /*const */ /*ldap_auth_info_t */ void *ldap_auth_info, const gchar *cacert)
67 {
69  LDAP *ldap = NULL;
70  gchar *dn = NULL;
71 
72  if (info == NULL || username == NULL || password == NULL || !info->ldap_host)
73  {
74  g_debug ("Not attempting ldap_connect: missing parameter.");
75  return -1;
76  }
77 
78  dn = ldap_auth_info_auth_dn (info, username);
79 
80  ldap = ldap_auth_bind (info->ldap_host, dn, password, !info->allow_plaintext,
81  cacert);
82 
83  if (ldap == NULL)
84  {
85  g_debug ("Could not bind to ldap host %s", info->ldap_host);
86  return -1;
87  }
88 
89  ldap_unbind_ext_s (ldap, NULL, NULL);
90 
91  return 0;
92 }
93 
109 ldap_auth_info_new (const gchar *ldap_host, const gchar *auth_dn,
110  gboolean allow_plaintext)
111 {
112  // Certain parameters might not be NULL.
113  if (!ldap_host || !auth_dn)
114  return NULL;
115 
116  if (ldap_auth_dn_is_good (auth_dn) == FALSE)
117  return NULL;
118 
119  ldap_auth_info_t info = g_malloc0 (sizeof (struct ldap_auth_info));
120  info->ldap_host = g_strdup (ldap_host);
121  info->auth_dn = g_strdup (auth_dn);
122  info->allow_plaintext = allow_plaintext;
123 
124  return info;
125 }
126 
132 void
134 {
135  if (!info)
136  return;
137 
138  g_free (info->ldap_host);
139  g_free (info->auth_dn);
140 
141  g_free (info);
142 }
143 
153 gchar *
154 ldap_auth_info_auth_dn (const ldap_auth_info_t info, const gchar *username)
155 {
156  if (info == NULL || username == NULL)
157  return NULL;
158 
159  gchar *dn = g_strdup_printf (info->auth_dn, username);
160 
161  return dn;
162 }
163 
177 LDAP *
178 ldap_auth_bind (const gchar *host, const gchar *userdn, const gchar *password,
179  gboolean force_encryption, const gchar *cacert)
180 {
181  LDAP *ldap = NULL;
182  int ldap_return = 0;
183  int ldapv3 = LDAP_VERSION3;
184  gchar *ldapuri = NULL;
185  struct berval credential;
186  gchar *name;
187  gint fd;
188 
189  if (host == NULL || userdn == NULL || password == NULL)
190  return NULL;
191 
192  // Prevent empty password, bind against ADS will succeed with
193  // empty password by default.
194  if (strlen (password) == 0)
195  return NULL;
196 
197  if (force_encryption == FALSE)
198  g_warning ("Allowed plaintext LDAP authentication.");
199 
200  if (cacert)
201  {
202  GError *error;
203 
204  error = NULL;
205  fd = g_file_open_tmp (NULL, &name, &error);
206  if (fd == -1)
207  {
208  g_warning ("Could not open temp file for LDAP CACERTFILE: %s",
209  error->message);
210  g_error_free (error);
211  }
212  else
213  {
214  if (g_chmod (name, 0600))
215  g_warning ("Could not chmod for LDAP CACERTFILE");
216 
217  g_file_set_contents (name, cacert, strlen (cacert), &error);
218  if (error)
219  {
220  g_warning ("Could not write LDAP CACERTFILE: %s", error->message);
221  g_error_free (error);
222  }
223  else
224  {
225  if (ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE, name)
226  != LDAP_OPT_SUCCESS)
227  g_warning ("Could not set LDAP CACERTFILE option.");
228  }
229  }
230  }
231  else
232  fd = -1;
233 
234  ldapuri = g_strconcat ("ldap://", host, NULL);
235 
236  ldap_return = ldap_initialize (&ldap, ldapuri);
237 
238  if (ldap == NULL || ldap_return != LDAP_SUCCESS)
239  {
240  g_warning ("Could not open LDAP connection for authentication.");
241  g_free (ldapuri);
242  goto fail;
243  }
244 
245  /* Fail if server doesn't talk LDAPv3 or StartTLS initialization fails. */
246  ldap_return = ldap_set_option (ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapv3);
247  if (ldap_return != LDAP_SUCCESS)
248  {
249  g_warning ("Aborting, could not set ldap protocol version to 3: %s.",
250  ldap_err2string (ldap_return));
251  g_free (ldapuri);
252  goto fail;
253  }
254 
255  ldap_return = ldap_start_tls_s (ldap, NULL, NULL);
256  if (ldap_return != LDAP_SUCCESS)
257  {
258  // Try ldaps.
259  g_warning ("StartTLS failed, trying to establish ldaps connection.");
260  g_free (ldapuri);
261  ldapuri = g_strconcat ("ldaps://", host, NULL);
262 
263  ldap_return = ldap_initialize (&ldap, ldapuri);
264  if (ldap == NULL || ldap_return != LDAP_SUCCESS)
265  {
266  if (force_encryption == TRUE)
267  {
268  g_warning ("Aborting ldap authentication: Could not init LDAP "
269  "StartTLS nor ldaps: %s.",
270  ldap_err2string (ldap_return));
271  g_free (ldapuri);
272  goto fail;
273  }
274  else
275  {
276  g_warning ("Could not init LDAP StartTLS, nor ldaps: %s.",
277  ldap_err2string (ldap_return));
278  g_warning (
279  "Reinit LDAP connection to do plaintext authentication");
280  ldap_unbind_ext_s (ldap, NULL, NULL);
281 
282  // Note that for connections to default ADS, a failed
283  // StartTLS negotiation breaks the future bind, so retry.
284  ldap_return = ldap_initialize (&ldap, ldapuri);
285  if (ldap == NULL || ldap_return != LDAP_SUCCESS)
286  {
287  g_warning (
288  "Could not reopen LDAP connection for authentication.");
289  g_free (ldapuri);
290  goto fail;
291  }
292  }
293  }
294  }
295  else
296  g_debug ("LDAP StartTLS initialized.");
297 
298  g_free (ldapuri);
299 
300  int do_search = 0;
301  LDAPDN dn = NULL;
302  gchar *use_dn = NULL;
303  gchar **uid = NULL;
304 
305  /* Validate the DN with the LDAP library. */
306  if (ldap_str2dn (userdn, &dn, LDAP_DN_FORMAT_LDAPV3) == LDAP_SUCCESS)
307  {
308  gchar **use_uid = NULL;
309  ldap_memfree (dn);
310  dn = NULL;
311  uid = g_strsplit (userdn, ",", 2);
312  use_uid = g_strsplit (uid[0], "=", 2);
313 
314  if (!g_strcmp0 (use_uid[0], "uid"))
315  do_search = 1;
316  else
317  {
318  g_strfreev (uid);
319  uid = NULL;
320  }
321  g_strfreev (use_uid);
322  use_uid = NULL;
323  }
324 
325  /* The uid attribute was given, so a search is performed. */
326  if (do_search)
327  {
328  /* Perform anonymous bind to search. */
329  credential.bv_val = NULL;
330  credential.bv_len = 0U;
331  ldap_return = ldap_sasl_bind_s (ldap, NULL, LDAP_SASL_SIMPLE, &credential,
332  NULL, NULL, NULL);
333  if (ldap_return != LDAP_SUCCESS)
334  {
335  g_warning ("LDAP anonymous authentication failure: %s",
336  ldap_err2string (ldap_return));
337  goto fail;
338  }
339  else
340  {
341  char *attrs[2] = {"dn", NULL};
342  LDAPMessage *result = NULL;
343  gchar **base = g_strsplit (userdn, ",", 2);
344 
345  /* search for the DN and unbind */
346  ldap_return =
347  ldap_search_ext_s (ldap, base[1], LDAP_SCOPE_SUBTREE, uid[0], attrs,
348  0, NULL, NULL, NULL, 1, &result);
349  g_strfreev (base);
350  base = NULL;
351  g_strfreev (uid);
352  uid = NULL;
353  if (ldap_return != LDAP_SUCCESS)
354  use_dn = g_strdup (userdn);
355  else
356  {
357  gchar *found_dn;
358  found_dn = ldap_get_dn (ldap, result);
359  if ((found_dn == NULL) || (strlen (found_dn) == 0U))
360  use_dn = g_strdup (userdn);
361  else
362  use_dn = g_strdup (found_dn);
363  ldap_memfree (found_dn);
364  }
365  ldap_msgfree (result);
366  }
367  }
368  else
369  use_dn = g_strdup (userdn);
370 
371  if (use_dn != NULL)
372  {
373  credential.bv_val = g_strdup (password);
374  credential.bv_len = strlen (password);
375  ldap_return = ldap_sasl_bind_s (ldap, use_dn, LDAP_SASL_SIMPLE,
376  &credential, NULL, NULL, NULL);
377  g_free (credential.bv_val);
378  g_free (use_dn);
379  if (ldap_return != LDAP_SUCCESS)
380  {
381  g_warning ("LDAP authentication failure: %s.",
382  ldap_err2string (ldap_return));
383  goto fail;
384  }
385 
386  if (fd > -1)
387  {
388  g_unlink (name);
389  close (fd);
390  g_free (name);
391  }
392  return ldap;
393  }
394 
395 fail:
396  if (fd > -1)
397  {
398  g_unlink (name);
399  close (fd);
400  g_free (name);
401  }
402  return NULL;
403 }
404 
412 gboolean
413 ldap_auth_dn_is_good (const gchar *authdn)
414 {
415  gchar *eg;
416  LDAPDN dn;
417  int ln = 0;
418 
419  if (authdn == NULL || authdn[0] == '\0')
420  return FALSE;
421 
422  // Must contain %s
423  if (!strstr (authdn, "%s"))
424  return FALSE;
425 
426  // Must not contain other %-signs
427  char *pos = strchr (authdn, '%');
428  pos = strchr (pos + 1, '%');
429  if (pos != NULL)
430  return FALSE;
431 
432  ln = strlen (authdn);
433 
434  // As a special exception allow ADS-style domain\user - pairs.
435  if (strchr (authdn, '\\') && authdn[ln - 2] == '%' && authdn[ln - 1] == 's')
436  return TRUE;
437 
438  // Also allow user@domain - pairs.
439  if (authdn[0] == '%' && authdn[1] == 's' && authdn[2] == '@')
440  return TRUE;
441 
442  /* Validate the DN with the LDAP library. */
443  eg = g_strdup_printf (authdn, "example");
444  dn = NULL;
445  if (ldap_str2dn (eg, &dn, LDAP_DN_FORMAT_LDAPV3))
446  {
447  g_free (eg);
448  return FALSE;
449  }
450  g_free (eg);
451  ldap_memfree (dn);
452 
453  return TRUE;
454 }
455 
456 #else
457 
472 ldap_auth_info_new (const gchar *ldap_host, const gchar *auth_dn,
473  gboolean allow_plaintext)
474 {
475  (void) ldap_host;
476  (void) auth_dn;
477  (void) allow_plaintext;
478  return NULL;
479 }
480 
491 int
493  const gchar *username, const gchar *password,
494  /*const */ /*ldap_auth_info_t */ void *ldap_auth_info, const gchar *cacert)
495 {
496  (void) username;
497  (void) password;
498  (void) ldap_auth_info;
499  (void) cacert;
500  return -1;
501 }
502 
508 void
510 {
511  (void) info;
512 }
513 
514 #endif /* ENABLE_LDAP_AUTH */
gchar * ldap_host
Address of the ldap server, might include port.
Definition: ldaputils.h:41
gchar * auth_dn
DN to authenticate with.
Definition: ldaputils.h:42
gboolean allow_plaintext
!Whether or not StartTLS is required.
Definition: ldaputils.h:43
Header for LDAP-Connect Authentication module.
struct ldap_auth_info * ldap_auth_info_t
Authentication schema and address type.
Definition: ldaputils.h:31
void ldap_auth_info_free(ldap_auth_info_t info)
Dummy function for Manager.
Definition: ldaputils.c:509
int ldap_connect_authenticate(const gchar *username, const gchar *password, void *ldap_auth_info, const gchar *cacert)
Dummy function for Manager.
Definition: ldaputils.c:492
Schema (dn) and info to use for a basic ldap authentication.
Definition: ldaputils.h:39
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:472