Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to force LDAPS for authentication #747

Merged
merged 2 commits into from
Mar 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
257 changes: 197 additions & 60 deletions util/ldaputils.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ ldap_connect_authenticate (

dn = ldap_auth_info_auth_dn (info, username);

ldap = ldap_auth_bind (info->ldap_host, dn, password, !info->allow_plaintext,
cacert);
ldap = ldap_auth_bind_2 (info->ldap_host, dn, password,
!info->allow_plaintext, cacert, info->ldaps_only);

if (ldap == NULL)
{
Expand Down Expand Up @@ -152,6 +152,28 @@ ldap_connect_authenticate (
ldap_auth_info_t
ldap_auth_info_new (const gchar *ldap_host, const gchar *auth_dn,
gboolean allow_plaintext)
{
return ldap_auth_info_new_2 (ldap_host, auth_dn, allow_plaintext, FALSE);
}

/**
* @brief Create a new ldap authentication schema and info.
*
* @param ldap_host Host to authenticate against. Might not be NULL,
* but empty.
* @param auth_dn DN where the actual user name is to be inserted at
* "%s", e.g. uid=%s,cn=users. Might not be NULL,
* but empty, has to contain a single %s.
* @param allow_plaintext If FALSE, require StartTLS initialization to
* succeed.
* @param ldaps_only Whether to only use LDAPS.
*
* @return Fresh ldap_auth_info_t, or NULL on error. Free with
* ldap_auth_info_free.
*/
ldap_auth_info_t
ldap_auth_info_new_2 (const gchar *ldap_host, const gchar *auth_dn,
gboolean allow_plaintext, gboolean ldaps_only)
{
// Certain parameters might not be NULL.
if (!ldap_host || !auth_dn)
Expand All @@ -164,6 +186,7 @@ ldap_auth_info_new (const gchar *ldap_host, const gchar *auth_dn,
info->ldap_host = g_strdup (ldap_host);
info->auth_dn = g_strdup (auth_dn);
info->allow_plaintext = allow_plaintext;
info->ldaps_only = ldaps_only;

return info;
}
Expand Down Expand Up @@ -206,7 +229,7 @@ ldap_auth_info_auth_dn (const ldap_auth_info_t info, const gchar *username)
}

/**
* @brief Setup and bind to an LDAP.
* @brief Setup and bind to an LDAP, trying StartTLS first.
*
* @param[in] host Host to connect to.
* @param[in] userdn DN to authenticate against
Expand All @@ -222,68 +245,36 @@ LDAP *
ldap_auth_bind (const gchar *host, const gchar *userdn, const gchar *password,
gboolean force_encryption, const gchar *cacert)
{
LDAP *ldap = NULL;
return ldap_auth_bind_2 (host, userdn, password, force_encryption, cacert,
FALSE);
}

/**
* @brief Try to init a connection to an LDAP server using StartTLS first.
*
* @param[in] host Host to connect to.
* @param[in] force_encryption Whether or not to abort if connection
* encryption via StartTLS or ldaps failed.
*
* @return The LDAP handle or NULL on failure
*/
static LDAP *
ldap_init_internal (const char *host, gboolean force_encryption)
{
LDAP *ldap;
gchar *ldapuri = NULL;
int ldap_return = 0;
int ldapv3 = LDAP_VERSION3;
gchar *ldapuri = NULL;
struct berval credential;
gchar *name;
gint fd;

if (host == NULL || userdn == NULL || password == NULL)
return NULL;

// Prevent empty password, bind against ADS will succeed with
// empty password by default.
if (strlen (password) == 0)
return NULL;

if (force_encryption == FALSE)
g_warning ("Allowed plaintext LDAP authentication.");

if (cacert)
{
GError *error;

error = NULL;
fd = g_file_open_tmp (NULL, &name, &error);
if (fd == -1)
{
g_warning ("Could not open temp file for LDAP CACERTFILE: %s",
error->message);
g_error_free (error);
}
else
{
if (g_chmod (name, 0600))
g_warning ("Could not chmod for LDAP CACERTFILE");

g_file_set_contents (name, cacert, strlen (cacert), &error);
if (error)
{
g_warning ("Could not write LDAP CACERTFILE: %s", error->message);
g_error_free (error);
}
else
{
if (ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE, name)
!= LDAP_OPT_SUCCESS)
g_warning ("Could not set LDAP CACERTFILE option.");
}
}
}
else
fd = -1;

ldapuri = g_strconcat ("ldap://", host, NULL);

ldap_return = ldap_initialize (&ldap, ldapuri);

if (ldap == NULL || ldap_return != LDAP_SUCCESS)
{
g_warning ("Could not open LDAP connection for authentication.");
g_warning ("Could not init LDAP connection for authentication.");
g_free (ldapuri);
goto fail;
return NULL;
}

/* Fail if server doesn't talk LDAPv3 or StartTLS initialization fails. */
Expand All @@ -293,7 +284,7 @@ ldap_auth_bind (const gchar *host, const gchar *userdn, const gchar *password,
g_warning ("Aborting, could not set ldap protocol version to 3: %s.",
ldap_err2string (ldap_return));
g_free (ldapuri);
goto fail;
return NULL;
}

ldap_return = ldap_start_tls_s (ldap, NULL, NULL);
Expand All @@ -313,7 +304,7 @@ ldap_auth_bind (const gchar *host, const gchar *userdn, const gchar *password,
"StartTLS nor ldaps: %s.",
ldap_err2string (ldap_return));
g_free (ldapuri);
goto fail;
return NULL;
}
else
{
Expand All @@ -331,7 +322,7 @@ ldap_auth_bind (const gchar *host, const gchar *userdn, const gchar *password,
g_warning (
"Could not reopen LDAP connection for authentication.");
g_free (ldapuri);
goto fail;
return NULL;
}
// Set LDAP version to 3 after initialization
ldap_return =
Expand All @@ -342,7 +333,7 @@ ldap_auth_bind (const gchar *host, const gchar *userdn, const gchar *password,
"Aborting, could not set ldap protocol version to 3: %s.",
ldap_err2string (ldap_return));
g_free (ldapuri);
goto fail;
return NULL;
}
}
}
Expand All @@ -357,7 +348,7 @@ ldap_auth_bind (const gchar *host, const gchar *userdn, const gchar *password,
"Aborting, could not set ldap protocol version to 3: %s.",
ldap_err2string (ldap_return));
g_free (ldapuri);
goto fail;
return NULL;
}
}
}
Expand All @@ -366,6 +357,127 @@ ldap_auth_bind (const gchar *host, const gchar *userdn, const gchar *password,

g_free (ldapuri);

return ldap;
}

/**
* @brief Try to init a connection to an LDAP server using only LDAPS.
*
* @param[in] host Host to connect to.
*
* @return The LDAP handle or NULL on failure
*/
static LDAP *
ldap_init_internal_ldaps_only (const char *host)
{
LDAP *ldap;
gchar *ldapuri = NULL;
int ldap_return = 0;
int ldapv3 = LDAP_VERSION3;

ldapuri = g_strconcat ("ldaps://", host, NULL);

ldap_return = ldap_initialize (&ldap, ldapuri);
if (ldap == NULL || ldap_return != LDAP_SUCCESS)
{
g_warning ("Could not init LDAPS connection for authentication.");
g_free (ldapuri);
return NULL;
}

/* Fail if server doesn't talk LDAPv3. */
ldap_return = ldap_set_option (ldap, LDAP_OPT_PROTOCOL_VERSION, &ldapv3);
if (ldap_return != LDAP_SUCCESS)
{
g_warning ("Aborting, could not set ldap protocol version to 3: %s.",
ldap_err2string (ldap_return));
g_free (ldapuri);
return NULL;
}

g_debug ("LDAPS initialized.");
g_free (ldapuri);
return ldap;
}

/**
* @brief Setup and bind to an LDAP.
*
* @param[in] host Host to connect to.
* @param[in] userdn DN to authenticate against
* @param[in] password Password for userdn.
* @param[in] force_encryption Whether or not to abort if connection
* encryption via StartTLS or ldaps failed.
* @param[in] cacert CA Certificate for LDAP_OPT_X_TLS_CACERTFILE,
* or NULL.
* @param[in] ldaps_only Whether to try only LDAPS.
*
* @return LDAP Handle or NULL if an error occurred, authentication failed etc.
*/
LDAP *
ldap_auth_bind_2 (const gchar *host, const gchar *userdn, const gchar *password,
gboolean force_encryption, const gchar *cacert,
gboolean ldaps_only)
{
LDAP *ldap;
int ldap_return;
struct berval credential;
gchar *name;
gint fd;

if (host == NULL || userdn == NULL || password == NULL)
return NULL;

// Prevent empty password, bind against ADS will succeed with
// empty password by default.
if (strlen (password) == 0)
return NULL;

if (force_encryption == FALSE)
g_warning ("Allowed plaintext LDAP authentication.");

if (cacert)
{
GError *error;

error = NULL;
fd = g_file_open_tmp (NULL, &name, &error);
if (fd == -1)
{
g_warning ("Could not open temp file for LDAP CACERTFILE: %s",
error->message);
g_error_free (error);
}
else
{
if (g_chmod (name, 0600))
g_warning ("Could not chmod for LDAP CACERTFILE");

g_file_set_contents (name, cacert, strlen (cacert), &error);
if (error)
{
g_warning ("Could not write LDAP CACERTFILE: %s", error->message);
g_error_free (error);
}
else
{
if (ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE, name)
!= LDAP_OPT_SUCCESS)
g_warning ("Could not set LDAP CACERTFILE option.");
}
}
}
else
fd = -1;

if (ldaps_only)
ldap = ldap_init_internal_ldaps_only (host);
else
ldap = ldap_init_internal (host, force_encryption);

if (ldap == NULL)
goto fail;

int do_search = 0;
LDAPDN dn = NULL;
gchar *use_dn = NULL;
Expand Down Expand Up @@ -559,6 +671,31 @@ ldap_auth_info_new (const gchar *ldap_host, const gchar *auth_dn,
return NULL;
}

/**
* @brief Dummy function for manager.
*
* @param ldap_host Host to authenticate against. Might not be NULL,
* but empty.
* @param auth_dn DN where the actual user name is to be inserted at
* "%s", e.g. uid=%s,cn=users. Might not be NULL,
* but empty, has to contain a single %s.
* @param allow_plaintext If FALSE, require StartTLS initialization to
* succeed.
* @param ldaps_only Whether to try LDAPS only.
*
* @return NULL.
*/
ldap_auth_info_t
ldap_auth_info_new_2 (const gchar *ldap_host, const gchar *auth_dn,
gboolean allow_plaintext, gboolean ldaps_only)
{
(void) ldap_host;
(void) auth_dn;
(void) allow_plaintext;
(void) ldaps_only;
return NULL;
}

/**
* @brief Dummy function for Manager.
*
Expand Down
10 changes: 9 additions & 1 deletion util/ldaputils.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ struct ldap_auth_info
{
gchar *ldap_host; ///< Address of the ldap server, might include port.
gchar *auth_dn; ///< DN to authenticate with.
gboolean allow_plaintext; ///< !Whether or not StartTLS is required.
gboolean allow_plaintext; ///< !Whether or not StartTLS or LDAPS is required.
gboolean ldaps_only; ///< Whether to try LDAPS before StartTLS.
};

int
Expand All @@ -55,6 +56,9 @@ void ldap_auth_info_free (ldap_auth_info_t);
ldap_auth_info_t
ldap_auth_info_new (const gchar *, const gchar *, gboolean);

ldap_auth_info_t
ldap_auth_info_new_2 (const gchar *, const gchar *, gboolean, gboolean);

#ifdef ENABLE_LDAP_AUTH

#include <ldap.h>
Expand All @@ -66,6 +70,10 @@ LDAP *
ldap_auth_bind (const gchar *, const gchar *, const gchar *, gboolean,
const gchar *);

LDAP *
ldap_auth_bind_2 (const gchar *, const gchar *, const gchar *, gboolean,
const gchar *, gboolean);

gboolean
ldap_auth_dn_is_good (const gchar *);

Expand Down