/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2005-2021 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
. */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#ifdef WITH_LDAP
#include "mailutils/argcv.h"
#include "mailutils/wordsplit.h"
#include "mailutils/assoc.h"
#include "mailutils/list.h"
#include "mailutils/iterator.h"
#include "mailutils/mailbox.h"
#include "mailutils/sql.h"
#include "mailutils/mu_auth.h"
#include "mailutils/error.h"
#include "mailutils/errno.h"
#include "mailutils/nls.h"
#include "mailutils/util.h"
#include "mailutils/stream.h"
#include "mailutils/filter.h"
#include "mailutils/md5.h"
#include "mailutils/sha1.h"
#include "mailutils/ldap.h"
#include
#include
const char *default_field_map =
"name=uid:"
"passwd=userPassword:"
"uid=uidNumber:"
"gid=gidNumber:"
"gecos=gecos:"
"dir=homeDirectory:"
"shell=loginShell";
static struct mu_ldap_module_config ldap_param;
static int
cb_field_map (void *data, mu_config_value_t *val)
{
char *err_term;
int rc = mu_cfg_field_map (val, &ldap_param.field_map, &err_term);
if (rc)
{
if (err_term)
mu_error (_("error near %s: %s"), err_term, mu_strerror (rc));
else
mu_error ("%s", mu_strerror (rc));
}
return rc;
}
static struct mu_cfg_param mu_ldap_param[] = {
{ "enable", mu_c_bool, &ldap_param.enable, 0, NULL,
N_("Enable LDAP lookups.") },
{ "url", mu_c_string, &ldap_param.url, 0, NULL,
N_("Set URL of the LDAP server."),
N_("url") },
{ "base", mu_c_string, &ldap_param.base, 0, NULL,
N_("Base DN for LDAP lookups."),
N_("dn") },
{ "binddn", mu_c_string, &ldap_param.binddn, 0, NULL,
N_("DN for accessing LDAP database."),
N_("dn") },
{ "passwd", mu_c_string, &ldap_param.passwd, 0, NULL,
N_("Password for use with binddn.") },
{ "tls", mu_c_bool, &ldap_param.tls, 0, NULL,
N_("Use TLS encryption.") },
{ "debug", mu_c_int, &ldap_param.debug, 0, NULL,
N_("Set LDAP debugging level.") },
{ "field-map", mu_cfg_callback, NULL, 0, cb_field_map,
N_("Set a field-map for parsing LDAP replies. The map is a "
"column-separated list of definitions. Each definition has the "
"following form:\n"
" =\n"
"where is one of the following: name, passwd, uid, gid, "
"gecos, dir, shell, mailbox, quota, and is the name of "
"the corresponding LDAP attribute."),
N_("map: definition") },
{ "getpwnam", mu_c_string, &ldap_param.getpwnam_filter, 0, NULL,
N_("LDAP filter to use for getpwnam requests."),
N_("filter") },
{ "getpwuid", mu_c_string, &ldap_param.getpwuid_filter, 0, NULL,
N_("LDAP filter to use for getpwuid requests."),
N_("filter") },
{ NULL }
};
int
mu_ldap_section_parser
(enum mu_cfg_section_stage stage, const mu_cfg_node_t *node,
const char *section_label, void **section_data,
void *call_data, mu_cfg_tree_t *tree)
{
switch (stage)
{
case mu_cfg_section_start:
ldap_param.enable = 1;
break;
default:
break;
}
return 0;
}
static void
module_init (void *ptr)
{
if (ldap_param.enable)
{
if (!ldap_param.getpwnam_filter)
ldap_param.getpwnam_filter =
"(&(objectClass=posixAccount) (uid=$user))";
if (!ldap_param.getpwuid_filter)
ldap_param.getpwuid_filter =
"(&(objectClass=posixAccount) (uidNumber=$user))";
if (!ldap_param.field_map)
{
struct mu_config_value val;
val.type = MU_CFG_STRING;
val.v.string = default_field_map;
if (mu_cfg_field_map (&val, &ldap_param.field_map, NULL))
abort ();
}
}
}
static int
_mu_conn_setup (LDAP **pld)
{
int rc;
LDAPURLDesc *ludlist, **ludp;
char **urls = NULL;
int nurls = 0;
char *ldapuri = NULL;
LDAP *ld = NULL;
int protocol = LDAP_VERSION3; /* FIXME: must be configurable */
if (ldap_param.debug)
{
if (ber_set_option (NULL, LBER_OPT_DEBUG_LEVEL, &ldap_param.debug)
!= LBER_OPT_SUCCESS )
mu_error (_("cannot set LBER_OPT_DEBUG_LEVEL %d"), ldap_param.debug);
if (ldap_set_option (NULL, LDAP_OPT_DEBUG_LEVEL, &ldap_param.debug)
!= LDAP_OPT_SUCCESS )
mu_error (_("could not set LDAP_OPT_DEBUG_LEVEL %d"),
ldap_param.debug);
}
if (ldap_param.url)
{
rc = ldap_url_parse (ldap_param.url, &ludlist);
if (rc != LDAP_URL_SUCCESS)
{
mu_error (_("cannot parse LDAP URL(s)=%s (%d)"),
ldap_param.url, rc);
return 1;
}
for (ludp = &ludlist; *ludp; )
{
LDAPURLDesc *lud = *ludp;
char **tmp;
if (lud->lud_dn && lud->lud_dn[0]
&& (lud->lud_host == NULL || lud->lud_host[0] == '\0'))
{
/* if no host but a DN is provided, try DNS SRV to gather the
host list */
char *domain = NULL, *hostlist = NULL;
size_t i;
struct mu_wordsplit ws;
if (ldap_dn2domain (lud->lud_dn, &domain) || !domain)
{
mu_error (_("DNS SRV: cannot convert DN=\"%s\" into a domain"),
lud->lud_dn );
goto dnssrv_free;
}
rc = ldap_domain2hostlist (domain, &hostlist);
if (rc)
{
mu_error (_("DNS SRV: cannot convert domain=%s into a hostlist"),
domain);
goto dnssrv_free;
}
if (mu_wordsplit (hostlist, &ws, MU_WRDSF_DEFFLAGS))
{
mu_error (_("DNS SRV: could not parse hostlist=\"%s\": %s"),
hostlist, mu_wordsplit_strerror (&ws));
goto dnssrv_free;
}
tmp = realloc (urls, sizeof(char *) * (nurls + ws.ws_wordc + 1));
if (!tmp)
{
mu_error ("DNS SRV %s", mu_strerror (errno));
goto dnssrv_free;
}
urls = tmp;
urls[nurls] = NULL;
for (i = 0; i < ws.ws_wordc; i++)
{
urls[nurls + i + 1] = NULL;
rc = mu_asprintf (&urls[nurls + i],
"%s://%s",
lud->lud_scheme, ws.ws_wordv[i]);
if (rc)
{
mu_error ("DNS SRV %s", mu_strerror (rc));
goto dnssrv_free;
}
}
nurls += i;
dnssrv_free:
mu_wordsplit_free (&ws);
ber_memfree (hostlist);
ber_memfree (domain);
}
else
{
tmp = realloc (urls, sizeof(char *) * (nurls + 2));
if (!tmp)
{
mu_error ("DNS SRV %s", mu_strerror (errno));
break;
}
urls = tmp;
urls[nurls + 1] = NULL;
urls[nurls] = ldap_url_desc2str (lud);
if (!urls[nurls])
{
mu_error ("DNS SRV %s", mu_strerror (errno));
break;
}
nurls++;
}
*ludp = lud->lud_next;
lud->lud_next = NULL;
ldap_free_urldesc (lud);
}
if (ludlist)
{
ldap_free_urldesc (ludlist);
return 1;
}
else if (!urls)
return 1;
rc = mu_argcv_string (nurls, urls, &ldapuri);
if (rc)
{
mu_error ("%s", mu_strerror (rc));
return 1;
}
ber_memvfree ((void **)urls);
}
mu_diag_output (MU_DIAG_INFO,
"constructed LDAP URI: %s", ldapuri ? ldapuri : "");
rc = ldap_initialize (&ld, ldapuri);
if (rc != LDAP_SUCCESS)
{
mu_error (_("cannot create LDAP session handle for URI=%s (%d): %s"),
ldapuri, rc, ldap_err2string (rc));
free (ldapuri);
return 1;
}
free (ldapuri);
ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &protocol);
if (ldap_param.tls)
{
rc = ldap_start_tls_s (ld, NULL, NULL);
if (rc != LDAP_SUCCESS)
{
char *msg = NULL;
ldap_get_option (ld,
LDAP_OPT_DIAGNOSTIC_MESSAGE,
(void*)&msg);
mu_error (_("ldap_start_tls failed: %s"), ldap_err2string (rc));
mu_error (_("TLS diagnostics: %s"), msg);
ldap_memfree (msg);
ldap_unbind_ext (ld, NULL, NULL);
return 1;
}
}
/* FIXME: Timeouts, SASL, etc. */
*pld = ld;
return 0;
}
static int
_mu_ldap_bind (LDAP *ld)
{
int msgid, err, rc;
LDAPMessage *result;
LDAPControl **ctrls;
char msgbuf[256];
char *matched = NULL;
char *info = NULL;
char **refs = NULL;
static struct berval passwd;
passwd.bv_val = ldap_param.passwd;
passwd.bv_len = passwd.bv_val ? strlen (passwd.bv_val) : 0;
msgbuf[0] = 0;
rc = ldap_sasl_bind (ld, ldap_param.binddn, LDAP_SASL_SIMPLE, &passwd,
NULL, NULL, &msgid);
if (msgid == -1)
{
mu_error ("ldap_sasl_bind(SIMPLE) failed: %s", ldap_err2string (rc));
return 1;
}
if (ldap_result (ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1)
{
mu_error ("ldap_result failed");
return 1;
}
rc = ldap_parse_result (ld, result, &err, &matched, &info, &refs,
&ctrls, 1);
if (rc != LDAP_SUCCESS)
{
mu_error ("ldap_parse_result failed: %s", ldap_err2string (rc));
return 1;
}
if (ctrls)
ldap_controls_free (ctrls);
if (err != LDAP_SUCCESS
|| msgbuf[0]
|| (matched && matched[0])
|| (info && info[0])
|| refs)
{
/* FIXME: Use debug output for that */
mu_error ("ldap_bind: %s (%d)%s", ldap_err2string (err), err, msgbuf);
if (matched && *matched)
mu_error ("matched DN: %s", matched);
if (info && *info)
mu_error ("additional info: %s", info);
if (refs && *refs)
{
int i;
mu_error ("referrals:");
for (i = 0; refs[i]; i++)
mu_error ("%s", refs[i]);
}
}
if (matched)
ber_memfree (matched);
if (info)
ber_memfree (info);
if (refs)
ber_memvfree ((void **)refs);
return err == LDAP_SUCCESS ? 0 : MU_ERR_FAILURE;
}
static void
_mu_ldap_unbind (LDAP *ld)
{
if (ld)
{
ldap_set_option (ld, LDAP_OPT_SERVER_CONTROLS, NULL);
ldap_unbind_ext (ld, NULL, NULL);
}
}
static int
_construct_attr_array (size_t *pargc, char ***pargv)
{
size_t count, i;
char **argv;
mu_iterator_t itr = NULL;
mu_assoc_count (ldap_param.field_map, &count);
if (count == 0)
return MU_ERR_FAILURE;
argv = calloc (count + 1, sizeof argv[0]);
mu_assoc_get_iterator (ldap_param.field_map, &itr);
for (i = 0, mu_iterator_first (itr); !mu_iterator_is_done (itr);
mu_iterator_next (itr), i++)
{
char *str;
mu_iterator_current (itr, (void**) &str);
if ((argv[i] = strdup (str)) == NULL)
{
mu_argcv_free (i, argv);
return ENOMEM;
}
}
mu_iterator_destroy (&itr);
argv[i] = NULL;
*pargc = count;
*pargv = argv;
return 0;
}
static void
get_quota (mu_off_t *pquota, const char *str)
{
size_t n;
char *p;
int rc = mu_strtosize (str, &p, &n);
switch (rc)
{
case 0:
*pquota = n;
break;
case ERANGE:
mu_error (_("quota value is out of allowed range: %s"), str);
break;
case MU_ERR_PARSE:
mu_error (_("bad quota value: %s, stopped at %s"), str, p);
break;
default:
mu_diag_funcall (MU_DIAG_ERROR, "mu_strtosize", str, rc);
}
}
static void
_free_partial_auth_data (struct mu_auth_data *d)
{
free (d->name);
free (d->passwd);
free (d->gecos);
free (d->dir);
free (d->shell);
free (d->mailbox);
}
static int
_assign_partial_auth_data (struct mu_auth_data *d, const char *key,
const char *val)
{
int rc = 0;
if (strcmp (key, MU_AUTH_NAME) == 0)
rc = (d->name = strdup (val)) ? 0 : errno;
else if (strcmp (key, MU_AUTH_PASSWD) == 0)
rc = (d->passwd = strdup (val)) ? 0 : errno;
else if (strcmp (key, MU_AUTH_UID) == 0)
d->uid = atoi (val);
else if (strcmp (key, MU_AUTH_GID) == 0)
d->gid = atoi (val);
else if (strcmp (key, MU_AUTH_GECOS) == 0)
rc = (d->gecos = strdup (val)) ? 0 : errno;
else if (strcmp (key, MU_AUTH_DIR) == 0)
rc = (d->dir = strdup (val)) ? 0 : errno;
else if (strcmp (key, MU_AUTH_SHELL) == 0)
rc = (d->shell = strdup (val)) ? 0 : errno;
else if (strcmp (key, MU_AUTH_MAILBOX) == 0)
rc = (d->mailbox = strdup (val)) ? 0 : errno;
else if (strcmp (key, MU_AUTH_QUOTA) == 0)
get_quota (&d->quota, val);
return rc;
}
static int
_mu_entry_to_auth_data (LDAP *ld, LDAPMessage *msg,
struct mu_auth_data **return_data)
{
int rc;
BerElement *ber = NULL;
struct berval bv;
char *ufn = NULL;
struct mu_auth_data d;
mu_iterator_t itr = NULL;
memset (&d, 0, sizeof d);
rc = ldap_get_dn_ber (ld, msg, &ber, &bv);
ufn = ldap_dn2ufn (bv.bv_val);
ldap_memfree (ufn);
mu_assoc_get_iterator (ldap_param.field_map, &itr);
for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
char *key;
char *attr;
struct berval **values;
mu_iterator_current_kv (itr, (const void **)&key, (void**) &attr);
values = ldap_get_values_len (ld, msg, attr);
if (!values || !values[0])
{
mu_error ("LDAP field `%s' (`%s') has NULL value",
key, attr);
_free_partial_auth_data (&d);
return MU_ERR_READ;
}
rc = _assign_partial_auth_data (&d, key, values[0]->bv_val);
ldap_value_free_len (values);
if (rc)
{
_free_partial_auth_data (&d);
return rc;
}
}
rc = mu_auth_data_alloc (return_data,
d.name,
d.passwd,
d.uid,
d.gid,
d.gecos,
d.dir,
d.shell,
d.mailbox,
1);
if (rc == 0)
mu_auth_data_set_quota (*return_data, d.quota);
_free_partial_auth_data (&d);
return rc;
}
static int
_mu_ldap_search (LDAP *ld, const char *filter_pat, const char *key,
struct mu_auth_data **return_data)
{
int rc;
char **attrs;
size_t nattrs;
LDAPMessage *res, *msg;
ber_int_t msgid;
char *filter_str;
rc = _construct_attr_array (&nattrs, &attrs);
if (rc)
return rc;
rc = mu_str_vexpand (&filter_str, filter_pat, "user", key, NULL);
if (rc)
{
mu_argcv_free (nattrs, attrs);
if (rc == MU_ERR_FAILURE)
{
mu_error (_("cannot expand line `%s': %s"), filter_pat,
filter_str);
free (filter_str);
}
else
{
mu_error (_("cannot expand line `%s': %s"), filter_pat,
mu_strerror (rc));
}
return rc;
}
rc = ldap_search_ext (ld, ldap_param.base, LDAP_SCOPE_SUBTREE,
filter_str, attrs, 0,
NULL, NULL, NULL, -1, &msgid);
free (filter_str);
mu_argcv_free (nattrs, attrs);
if (rc != LDAP_SUCCESS)
{
mu_error ("ldap_search_ext: %s", ldap_err2string (rc));
if (rc == LDAP_NO_SUCH_OBJECT)
return MU_ERR_NOENT;
else
return MU_ERR_FAILURE;
}
rc = ldap_result (ld, msgid, LDAP_MSG_ALL, NULL, &res);
if (rc < 0)
{
mu_error ("ldap_result failed");
return MU_ERR_FAILURE;
}
rc = ldap_count_entries (ld, res);
if (rc == 0)
{
mu_error ("not enough entires");
return MU_ERR_NOENT;
}
if (rc > 1)
mu_error ("LDAP: too many entries for key %s", key);
msg = ldap_first_entry (ld, res);
rc = _mu_entry_to_auth_data (ld, msg, return_data);
ldap_msgfree (res);
return rc;
}
typedef int (*pwcheck_fp) (const char *, const char *);
static int
chk_crypt (const char *db_pass, const char *pass)
{
return strcmp (db_pass, crypt (pass, db_pass)) == 0 ?
0 : MU_ERR_AUTH_FAILURE;
}
static int
chk_md5 (const char *db_pass, const char *pass)
{
unsigned char md5digest[16];
unsigned char d1[16];
struct mu_md5_ctx md5context;
mu_stream_t str = NULL, flt = NULL;
mu_md5_init_ctx (&md5context);
mu_md5_process_bytes (pass, strlen (pass), &md5context);
mu_md5_finish_ctx (&md5context, md5digest);
mu_static_memory_stream_create (&str, db_pass, strlen (db_pass));
mu_filter_create (&flt, str, "base64", MU_FILTER_DECODE, MU_STREAM_READ);
mu_stream_unref (str);
mu_stream_read (flt, (char*) d1, sizeof d1, NULL);
mu_stream_destroy (&flt);
return memcmp (md5digest, d1, sizeof md5digest) == 0 ?
0 : MU_ERR_AUTH_FAILURE;
}
static int
chk_smd5 (const char *db_pass, const char *pass)
{
int rc;
unsigned char md5digest[16];
unsigned char *d1;
struct mu_md5_ctx md5context;
mu_stream_t str = NULL, flt = NULL;
size_t size;
size = strlen (db_pass);
mu_static_memory_stream_create (&str, db_pass, size);
mu_filter_create (&flt, str, "base64", MU_FILTER_DECODE, MU_STREAM_READ);
mu_stream_unref (str);
d1 = malloc (size);
if (!d1)
{
mu_stream_destroy (&flt);
return ENOMEM;
}
mu_stream_read (flt, (char*) d1, size, &size);
mu_stream_destroy (&flt);
if (size <= 16)
{
mu_error ("malformed SMD5 password: %s", db_pass);
return MU_ERR_FAILURE;
}
mu_md5_init_ctx (&md5context);
mu_md5_process_bytes (pass, strlen (pass), &md5context);
mu_md5_process_bytes (d1 + 16, size - 16, &md5context);
mu_md5_finish_ctx (&md5context, md5digest);
rc = memcmp (md5digest, d1, sizeof md5digest) == 0 ?
0 : MU_ERR_AUTH_FAILURE;
free (d1);
return rc;
}
static int
chk_sha (const char *db_pass, const char *pass)
{
unsigned char sha1digest[20];
unsigned char d1[20];
mu_stream_t str = NULL, flt = NULL;
struct mu_sha1_ctx sha1context;
mu_sha1_init_ctx (&sha1context);
mu_sha1_process_bytes (pass, strlen (pass), &sha1context);
mu_sha1_finish_ctx (&sha1context, sha1digest);
mu_static_memory_stream_create (&str, db_pass, strlen (db_pass));
mu_filter_create (&flt, str, "base64", MU_FILTER_DECODE, MU_STREAM_READ);
mu_stream_unref (str);
mu_stream_read (flt, (char*) d1, sizeof d1, NULL);
mu_stream_destroy (&flt);
return memcmp (sha1digest, d1, sizeof sha1digest) == 0 ?
0 : MU_ERR_AUTH_FAILURE;
}
static int
chk_ssha (const char *db_pass, const char *pass)
{
int rc;
unsigned char sha1digest[20];
unsigned char *d1;
struct mu_sha1_ctx sha1context;
mu_stream_t str = NULL, flt = NULL;
size_t size;
size = strlen (db_pass);
mu_static_memory_stream_create (&str, db_pass, size);
mu_filter_create (&flt, str, "base64", MU_FILTER_DECODE, MU_STREAM_READ);
mu_stream_unref (str);
d1 = malloc (size);
if (!d1)
{
mu_stream_destroy (&flt);
return ENOMEM;
}
mu_stream_read (flt, (char*) d1, size, &size);
mu_stream_destroy (&flt);
if (size <= 16)
{
mu_error ("malformed SSHA1 password: %s", db_pass);
return MU_ERR_FAILURE;
}
mu_sha1_init_ctx (&sha1context);
mu_sha1_process_bytes (pass, strlen (pass), &sha1context);
mu_sha1_process_bytes (d1 + 20, size - 20, &sha1context);
mu_sha1_finish_ctx (&sha1context, sha1digest);
rc = memcmp (sha1digest, d1, sizeof sha1digest) == 0 ?
0 : MU_ERR_AUTH_FAILURE;
free (d1);
return rc;
}
static struct passwd_algo
{
char *algo;
size_t len;
pwcheck_fp pwcheck;
} pwtab[] = {
#define DP(s, f) { #s, sizeof (#s) - 1, f }
DP (CRYPT, chk_crypt),
DP (MD5, chk_md5),
DP (SMD5, chk_smd5),
DP (SHA, chk_sha),
DP (SSHA, chk_ssha),
{ NULL }
#undef DP
};
static pwcheck_fp
find_pwcheck (const char *algo, int len)
{
struct passwd_algo *p;
for (p = pwtab; p->algo; p++)
if (len == p->len && mu_c_strncasecmp (p->algo, algo, len) == 0)
return p->pwcheck;
return NULL;
}
static int
mu_ldap_authenticate (struct mu_auth_data **return_data MU_ARG_UNUSED,
const void *key,
void *func_data MU_ARG_UNUSED, void *call_data)
{
const struct mu_auth_data *auth_data = key;
char *db_pass = auth_data->passwd;
char *pass = call_data;
if (auth_data->passwd == NULL || !pass)
return EINVAL;
if (db_pass[0] == '{')
{
int len;
char *algo;
pwcheck_fp pwcheck;
algo = db_pass + 1;
for (len = 0; algo[len] != '}'; len++)
if (algo[len] == 0)
{
/* Possibly malformed password, try plaintext anyway */
return strcmp (db_pass, pass) == 0 ? 0 : MU_ERR_FAILURE;
}
db_pass = algo + len + 1;
pwcheck = find_pwcheck (algo, len);
if (pwcheck)
return pwcheck (db_pass, pass);
else
{
mu_error ("Unsupported password algorithm scheme: %.*s",
len, algo);
return MU_ERR_FAILURE;
}
}
return strcmp (db_pass, pass) == 0 ? 0 : MU_ERR_AUTH_FAILURE;
}
static int
mu_auth_ldap_user_by_name (struct mu_auth_data **return_data,
const void *key,
void *func_data MU_ARG_UNUSED,
void *call_data MU_ARG_UNUSED)
{
int rc;
LDAP *ld;
if (!ldap_param.enable)
return ENOSYS;
if (_mu_conn_setup (&ld))
return MU_ERR_FAILURE;
if (_mu_ldap_bind (ld))
return MU_ERR_FAILURE;
rc = _mu_ldap_search (ld, ldap_param.getpwnam_filter, key, return_data);
_mu_ldap_unbind (ld);
return rc;
}
static int
mu_auth_ldap_user_by_uid (struct mu_auth_data **return_data,
const void *key,
void *func_data MU_ARG_UNUSED,
void *call_data MU_ARG_UNUSED)
{
int rc;
LDAP *ld;
char uidstr[128];
if (!ldap_param.enable)
return ENOSYS;
if (_mu_conn_setup (&ld))
return MU_ERR_FAILURE;
if (_mu_ldap_bind (ld))
return MU_ERR_FAILURE;
snprintf (uidstr, sizeof (uidstr), "%u", *(uid_t*)key);
rc = _mu_ldap_search (ld, ldap_param.getpwuid_filter, uidstr, return_data);
_mu_ldap_unbind (ld);
return rc;
}
#else
# define module_init NULL
# define mu_ldap_authenticate mu_auth_nosupport
# define mu_auth_ldap_user_by_name mu_auth_nosupport
# define mu_auth_ldap_user_by_uid mu_auth_nosupport
# define mu_ldap_param NULL
#endif
struct mu_auth_module mu_auth_ldap_module = {
.name = "ldap",
.commit = module_init,
.handler = {
[mu_auth_authenticate] = mu_ldap_authenticate,
[mu_auth_getpwnam] = mu_auth_ldap_user_by_name,
[mu_auth_getpwuid] = mu_auth_ldap_user_by_uid
},
.cfg = mu_ldap_param
};