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