1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3
/* Copyright (C) 2001-2004 Novell, Inc.
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of version 2 of the GNU Lesser General Public
7
* License as published by the Free Software Foundation.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* General Public License for more details.
14
* You should have received a copy of the GNU Lesser General Public
15
* License along with this program; if not, write to the
16
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
* Boston, MA 02110-1301, USA.
24
#include "e2k-global-catalog-ldap.h"
26
#include "e2k-utils.h"
33
#ifdef HAVE_LDAP_NTLM_BIND
38
static gboolean e2k_gc_debug = FALSE;
39
#define E2K_GC_DEBUG_MSG(x) if (e2k_gc_debug) printf x
41
#define E2K_GC_DEBUG_MSG(x)
44
struct _E2kGlobalCatalogPrivate {
49
GHashTable *entry_cache, *server_cache;
51
gchar *server, *user, *nt_domain, *password;
52
E2kAutoconfigGalAuthPref auth;
55
#define PARENT_TYPE G_TYPE_OBJECT
56
static GObjectClass *parent_class = NULL;
58
static void finalize (GObject *);
59
static gint get_gc_connection (E2kGlobalCatalog *gc, E2kOperation *op);
62
class_init (GObjectClass *object_class)
65
gchar *e2k_debug = getenv ("E2K_DEBUG");
67
if (e2k_debug && atoi (e2k_debug) > 3)
71
/* For some reason, sasl_client_init (called by ldap_init
72
* below) takes a *really* long time to scan the sasl modules
73
* when running under gdb. We're not using sasl anyway, so...
75
g_setenv ("SASL_PATH", "", TRUE);
77
parent_class = g_type_class_ref (PARENT_TYPE);
79
/* virtual method override */
80
object_class->finalize = finalize;
84
init (GObject *object)
86
E2kGlobalCatalog *gc = E2K_GLOBAL_CATALOG (object);
88
gc->priv = g_new0 (E2kGlobalCatalogPrivate, 1);
89
gc->priv->ldap_lock = g_mutex_new ();
90
gc->priv->entries = g_ptr_array_new ();
91
gc->priv->entry_cache = g_hash_table_new (e2k_ascii_strcase_hash,
92
e2k_ascii_strcase_equal);
93
gc->priv->server_cache = g_hash_table_new (g_str_hash, g_str_equal);
97
free_entry (E2kGlobalCatalogEntry *entry)
102
g_free (entry->display_name);
105
g_object_unref (entry->sid);
107
g_free (entry->email);
108
g_free (entry->mailbox);
110
if (entry->delegates) {
111
for (i = 0; i < entry->delegates->len; i++)
112
g_free (entry->delegates->pdata[i]);
113
g_ptr_array_free (entry->delegates, TRUE);
115
if (entry->delegators) {
116
for (i = 0; i < entry->delegators->len; i++)
117
g_free (entry->delegators->pdata[i]);
118
g_ptr_array_free (entry->delegators, TRUE);
125
free_server (gpointer key, gpointer value, gpointer data)
132
finalize (GObject *object)
134
E2kGlobalCatalog *gc = E2K_GLOBAL_CATALOG (object);
139
ldap_unbind (gc->priv->ldap);
141
for (i = 0; i < gc->priv->entries->len; i++)
142
free_entry (gc->priv->entries->pdata[i]);
143
g_ptr_array_free (gc->priv->entries, TRUE);
145
g_hash_table_foreach (gc->priv->server_cache, free_server, NULL);
146
g_hash_table_destroy (gc->priv->server_cache);
148
g_free (gc->priv->server);
149
g_free (gc->priv->user);
150
g_free (gc->priv->nt_domain);
151
if (gc->priv->password) {
152
memset (gc->priv->password, 0, strlen (gc->priv->password));
153
g_free (gc->priv->password);
156
g_mutex_free (gc->priv->ldap_lock);
165
G_OBJECT_CLASS (parent_class)->finalize (object);
168
E2K_MAKE_TYPE (e2k_global_catalog, E2kGlobalCatalog, class_init, init, PARENT_TYPE)
171
gc_ldap_result (LDAP *ldap, E2kOperation *op,
172
gint msgid, LDAPMessage **msg)
175
gint status, ldap_error;
181
status = ldap_result (ldap, msgid, TRUE, &tv, msg);
183
ldap_get_option (ldap, LDAP_OPT_ERROR_NUMBER,
187
} while (status == 0 && !e2k_operation_is_cancelled (op));
189
if (e2k_operation_is_cancelled (op)) {
190
ldap_abandon (ldap, msgid);
191
return LDAP_USER_CANCELLED;
197
gc_search (E2kGlobalCatalog *gc, E2kOperation *op,
198
const gchar *base, gint scope, const gchar *filter,
199
const gchar **attrs, LDAPMessage **msg)
201
gint ldap_error, msgid, try;
203
for (try = 0; try < 2; try++) {
204
ldap_error = get_gc_connection (gc, op);
205
if (ldap_error != LDAP_SUCCESS)
207
ldap_error = ldap_search_ext (gc->priv->ldap, base, scope,
208
filter, (gchar **)attrs,
209
FALSE, NULL, NULL, NULL, 0,
211
if (ldap_error == LDAP_SERVER_DOWN)
213
else if (ldap_error != LDAP_SUCCESS)
216
ldap_error = gc_ldap_result (gc->priv->ldap, op, msgid, msg);
217
if (ldap_error == LDAP_SERVER_DOWN)
219
else if (ldap_error != LDAP_SUCCESS)
225
return LDAP_SERVER_DOWN;
228
#ifdef HAVE_LDAP_NTLM_BIND
230
ntlm_bind (E2kGlobalCatalog *gc, E2kOperation *op, LDAP *ldap)
233
gint ldap_error, msgid, err;
234
gchar *nonce, *default_domain;
236
struct berval ldap_buf;
238
/* Create and send NTLM request */
239
ba = xntlm_negotiate ();
240
ldap_buf.bv_len = ba->len;
241
ldap_buf.bv_val = (gchar *) ba->data;
242
ldap_error = ldap_ntlm_bind (ldap, "NTLM", LDAP_AUTH_NTLM_REQUEST,
243
&ldap_buf, NULL, NULL, &msgid);
244
g_byte_array_free (ba, TRUE);
245
if (ldap_error != LDAP_SUCCESS) {
246
E2K_GC_DEBUG_MSG(("GC: Failure sending first NTLM bind message: 0x%02x\n", ldap_error));
250
/* Extract challenge */
251
ldap_error = gc_ldap_result (ldap, op, msgid, &msg);
252
if (ldap_error != LDAP_SUCCESS) {
253
E2K_GC_DEBUG_MSG(("GC: Could not parse first NTLM bind response\n"));
256
ldap_error = ldap_parse_ntlm_bind_result (ldap, msg, &ldap_buf);
258
if (ldap_error != LDAP_SUCCESS) {
259
E2K_GC_DEBUG_MSG(("GC: Could not parse NTLM bind response: 0x%02x\n", ldap_error));
263
if (!xntlm_parse_challenge (ldap_buf.bv_val, ldap_buf.bv_len,
264
&nonce, &default_domain,
266
E2K_GC_DEBUG_MSG(("GC: Could not find nonce in NTLM bind response\n"));
267
ber_memfree (ldap_buf.bv_val);
269
return LDAP_DECODING_ERROR;
271
ber_memfree (ldap_buf.bv_val);
273
/* Create and send response */
274
ba = xntlm_authenticate (nonce, gc->priv->nt_domain ? gc->priv->nt_domain : default_domain,
275
gc->priv->user, gc->priv->password, NULL);
276
ldap_buf.bv_len = ba->len;
277
ldap_buf.bv_val = (gchar *) ba->data;
278
ldap_error = ldap_ntlm_bind (ldap, "NTLM", LDAP_AUTH_NTLM_RESPONSE,
279
&ldap_buf, NULL, NULL, &msgid);
280
g_byte_array_free (ba, TRUE);
282
g_free (default_domain);
283
if (ldap_error != LDAP_SUCCESS) {
284
E2K_GC_DEBUG_MSG(("GC: Failure sending second NTLM bind message: 0x%02x\n", ldap_error));
288
/* And get the final result */
289
ldap_error = gc_ldap_result (ldap, op, msgid, &msg);
290
if (ldap_error != LDAP_SUCCESS) {
291
E2K_GC_DEBUG_MSG(("GC: Could not parse second NTLM bind response\n"));
294
ldap_error = ldap_parse_result (ldap, msg, &err, NULL, NULL,
296
if (ldap_error != LDAP_SUCCESS) {
297
E2K_GC_DEBUG_MSG(("GC: Could not parse second NTLM bind response: 0x%02x\n", ldap_error));
306
connect_ldap (E2kGlobalCatalog *gc, E2kOperation *op, LDAP *ldap)
311
SEC_WINNT_AUTH_IDENTITY_W auth;
315
#ifdef HAVE_LDAP_NTLM_BIND
316
if ((gc->priv->auth == E2K_AUTOCONFIG_USE_GAL_DEFAULT || gc->priv->auth == E2K_AUTOCONFIG_USE_GAL_NTLM)) {
317
ldap_error = ntlm_bind (gc, op, ldap);
318
if (ldap_error == LDAP_SUCCESS) {
319
E2K_GC_DEBUG_MSG(("GC: connected via NTLM\n\n"));
321
} else if (gc->priv->auth == E2K_AUTOCONFIG_USE_GAL_NTLM) {
322
E2K_GC_DEBUG_MSG(("GC: user setup NTLM, but it failed 0x%02x\n", ldap_error));
328
if (gc->priv->auth == E2K_AUTOCONFIG_USE_GAL_NTLM) {
329
E2K_GC_DEBUG_MSG(("GC: user setup NTLM, but we do not have it\n"));
331
return LDAP_AUTH_METHOD_NOT_SUPPORTED;
334
nt_name = gc->priv->nt_domain ?
335
g_strdup_printf ("%s\\%s", gc->priv->nt_domain, gc->priv->user) :
336
g_strdup (gc->priv->user);
338
ldap_error = ldap_simple_bind_s (ldap, nt_name, gc->priv->password);
340
auth.User = g_utf8_to_utf16 (gc->priv->user, -1, NULL, NULL, NULL);
341
auth.UserLength = wcslen (auth.User);
342
auth.Domain = gc->priv->nt_domain ?
343
g_utf8_to_utf16 (gc->priv->nt_domain, -1, NULL, NULL, NULL) :
344
g_utf8_to_utf16 ("", -1, NULL, NULL, NULL);
345
auth.DomainLength = wcslen (auth.Domain);
346
auth.Password = g_utf8_to_utf16 (gc->priv->password, -1, NULL, NULL, NULL);
347
auth.PasswordLength = wcslen (auth.Password);
348
auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
349
ldap_error = ldap_bind_s (ldap, nt_name, &auth, gc->priv->auth == E2K_AUTOCONFIG_USE_GAL_BASIC ? LDAP_AUTH_SIMPLE : LDAP_AUTH_NTLM);
350
g_free (auth.Password);
351
g_free (auth.Domain);
356
if (ldap_error != LDAP_SUCCESS)
357
g_warning ("LDAP authentication failed (0x%02x (%s))", ldap_error, ldap_err2string (ldap_error) ? ldap_err2string (ldap_error) : "Unknown error");
359
E2K_GC_DEBUG_MSG(("GC: connected\n\n"));
365
get_ldap_connection (E2kGlobalCatalog *gc, E2kOperation *op,
366
const gchar *server, gint port,
369
gint ldap_opt, ldap_error;
371
E2K_GC_DEBUG_MSG(("\nGC: Connecting to ldap://%s:%d/\n", server, port));
373
*ldap = ldap_init (server, port);
375
E2K_GC_DEBUG_MSG(("GC: failed\n\n"));
376
g_warning ("Could not connect to ldap://%s:%d/",
378
return LDAP_SERVER_DOWN;
382
ldap_opt = LDAP_DEREF_ALWAYS;
383
ldap_set_option (*ldap, LDAP_OPT_DEREF, &ldap_opt);
384
ldap_opt = gc->response_limit;
385
ldap_set_option (*ldap, LDAP_OPT_SIZELIMIT, &ldap_opt);
386
ldap_opt = LDAP_VERSION3;
387
ldap_set_option (*ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_opt);
389
ldap_error = connect_ldap (gc, op, *ldap);
390
if (ldap_error != LDAP_SUCCESS) {
398
get_gc_connection (E2kGlobalCatalog *gc, E2kOperation *op)
402
if (gc->priv->ldap) {
403
ldap_get_option (gc->priv->ldap, LDAP_OPT_ERROR_NUMBER, &err);
404
if (err != LDAP_SERVER_DOWN)
407
return connect_ldap (gc, op, gc->priv->ldap);
409
return get_ldap_connection (gc, op,
410
gc->priv->server, 3268,
416
* e2k_global_catalog_get_ldap:
417
* @gc: the global catalog
418
* @op: pointer to an initialized #E2kOperation to use for cancellation
419
* @ldap_error: set the value returned from get_ldap_connection if not NULL
421
* Returns a new LDAP handle. The caller must ldap_unbind() it when it
424
* Return value: an LDAP handle, or %NULL if it can't connect
427
e2k_global_catalog_get_ldap (E2kGlobalCatalog *gc, E2kOperation *op, gint *ldap_error)
432
g_return_val_if_fail (E2K_IS_GLOBAL_CATALOG (gc), NULL);
434
err = get_ldap_connection (gc, op, gc->priv->server, 3268, &ldap);
443
* e2k_global_catalog_new:
444
* @server: the GC server name
445
* @response_limit: the maximum number of responses to return from a search
446
* @user: username to authenticate with
447
* @domain: NT domain of @user, or %NULL to autodetect.
448
* @password: password to authenticate with
450
* Create an object for communicating with the Windows Global Catalog
453
* Return value: the new E2kGlobalCatalog. (This call will always succeed.
454
* If the passed-in data is bad, it will fail on a later call.)
457
e2k_global_catalog_new (const gchar *server, gint response_limit,
458
const gchar *user, const gchar *domain,
459
const gchar *password, E2kAutoconfigGalAuthPref use_auth)
461
E2kGlobalCatalog *gc;
463
gc = g_object_new (E2K_TYPE_GLOBAL_CATALOG, NULL);
464
gc->priv->server = g_strdup (server);
465
gc->priv->auth = use_auth;
466
gc->priv->user = g_strdup (user);
467
gc->priv->nt_domain = g_strdup (domain);
468
gc->priv->password = g_strdup (password);
469
gc->response_limit = response_limit;
475
lookup_mta (E2kGlobalCatalog *gc, E2kOperation *op, const gchar *mta_dn)
477
gchar *hostname, **values;
478
const gchar *attrs[2];
482
/* Skip over "CN=Microsoft MTA," */
483
mta_dn = strchr (mta_dn, ',');
488
hostname = g_hash_table_lookup (gc->priv->server_cache, mta_dn);
492
E2K_GC_DEBUG_MSG(("GC: Finding hostname for %s\n", mta_dn));
494
attrs[0] = "networkAddress";
497
ldap_error = gc_search (gc, op, mta_dn, LDAP_SCOPE_BASE,
499
if (ldap_error != LDAP_SUCCESS) {
500
E2K_GC_DEBUG_MSG(("GC: lookup failed (0x%02x)\n", ldap_error));
504
values = ldap_get_values (gc->priv->ldap, resp, "networkAddress");
507
E2K_GC_DEBUG_MSG(("GC: entry has no networkAddress\n"));
512
for (i = 0; values[i]; i++) {
513
if (strstr (values[i], "_tcp")) {
514
hostname = strchr (values[i], ':');
519
E2K_GC_DEBUG_MSG(("GC: host is not availble by TCP?\n"));
520
ldap_value_free (values);
524
hostname = g_strdup (hostname + 1);
525
g_hash_table_insert (gc->priv->server_cache, g_strdup (mta_dn), hostname);
526
ldap_value_free (values);
528
E2K_GC_DEBUG_MSG(("GC: %s\n", hostname));
533
get_sid_values (E2kGlobalCatalog *gc, E2kOperation *op,
534
LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
537
struct berval **bsid_values;
540
values = ldap_get_values (gc->priv->ldap, msg, "displayName");
542
E2K_GC_DEBUG_MSG(("GC: displayName %s\n", values[0]));
543
entry->display_name = g_strdup (values[0]);
544
ldap_value_free (values);
547
bsid_values = ldap_get_values_len (gc->priv->ldap, msg, "objectSid");
550
if (bsid_values[0]->bv_len < 2 ||
551
bsid_values[0]->bv_len != E2K_SID_BINARY_SID_LEN (bsid_values[0]->bv_val)) {
552
E2K_GC_DEBUG_MSG(("GC: invalid SID\n"));
556
values = ldap_get_values (gc->priv->ldap, msg, "objectCategory");
557
if (values && values[0] && !g_ascii_strncasecmp (values[0], "CN=Group", 8))
558
type = E2K_SID_TYPE_GROUP;
559
else if (values && values[0] && !g_ascii_strncasecmp (values[0], "CN=Foreign", 10))
560
type = E2K_SID_TYPE_WELL_KNOWN_GROUP;
562
type = E2K_SID_TYPE_USER;
564
ldap_value_free (values);
566
entry->sid = e2k_sid_new_from_binary_sid (
567
type, (guint8 *) bsid_values[0]->bv_val, entry->display_name);
568
entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_SID;
570
ldap_value_free_len (bsid_values);
574
get_mail_values (E2kGlobalCatalog *gc, E2kOperation *op,
575
LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
577
gchar **values, **mtavalues;
579
values = ldap_get_values (gc->priv->ldap, msg, "mail");
581
E2K_GC_DEBUG_MSG(("GC: mail %s\n", values[0]));
582
entry->email = g_strdup (values[0]);
583
g_hash_table_insert (gc->priv->entry_cache,
584
entry->email, entry);
585
entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_EMAIL;
586
ldap_value_free (values);
589
values = ldap_get_values (gc->priv->ldap, msg, "mailNickname");
590
mtavalues = ldap_get_values (gc->priv->ldap, msg, "homeMTA");
591
if (values && mtavalues) {
592
E2K_GC_DEBUG_MSG(("GC: mailNickname %s\n", values[0]));
593
E2K_GC_DEBUG_MSG(("GC: homeMTA %s\n", mtavalues[0]));
594
entry->exchange_server = (gchar *)lookup_mta (gc, op, mtavalues[0]);
595
ldap_value_free (mtavalues);
596
if (entry->exchange_server)
597
entry->mailbox = g_strdup (values[0]);
598
ldap_value_free (values);
599
entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_MAILBOX;
602
values = ldap_get_values (gc->priv->ldap, msg, "legacyExchangeDN");
604
E2K_GC_DEBUG_MSG(("GC: legacyExchangeDN %s\n", values[0]));
605
entry->legacy_exchange_dn = g_strdup (values[0]);
606
g_hash_table_insert (gc->priv->entry_cache,
607
entry->legacy_exchange_dn,
609
entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_LEGACY_EXCHANGE_DN;
610
ldap_value_free (values);
615
get_delegation_values (E2kGlobalCatalog *gc, E2kOperation *op,
616
LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
621
values = ldap_get_values (gc->priv->ldap, msg, "publicDelegates");
623
E2K_GC_DEBUG_MSG(("GC: publicDelegates\n"));
624
entry->delegates = g_ptr_array_new ();
625
for (i = 0; values[i]; i++) {
626
E2K_GC_DEBUG_MSG(("GC: %s\n", values[i]));
627
g_ptr_array_add (entry->delegates,
628
g_strdup (values[i]));
630
entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_DELEGATES;
631
ldap_value_free (values);
633
values = ldap_get_values (gc->priv->ldap, msg, "publicDelegatesBL");
635
E2K_GC_DEBUG_MSG(("GC: publicDelegatesBL\n"));
636
entry->delegators = g_ptr_array_new ();
637
for (i = 0; values[i]; i++) {
638
E2K_GC_DEBUG_MSG(("GC: %s\n", values[i]));
639
g_ptr_array_add (entry->delegators,
640
g_strdup (values[i]));
642
entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_DELEGATORS;
643
ldap_value_free (values);
648
get_quota_values (E2kGlobalCatalog *gc, E2kOperation *op,
649
LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
651
gchar **quota_setting_values, **quota_limit_values;
653
/* Check if mailbox store default values are used */
654
quota_setting_values = ldap_get_values (gc->priv->ldap, msg, "mDBUseDefaults");
655
if (!quota_setting_values) {
656
entry->quota_warn = entry->quota_nosend = entry->quota_norecv = 0;
660
entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_QUOTA;
661
E2K_GC_DEBUG_MSG(("GC: mDBUseDefaults %s\n", quota_setting_values[0]));
663
if (!strcmp (quota_setting_values[0], "TRUE")) {
664
/* use global mailbox store settings */
665
E2K_GC_DEBUG_MSG(("GC: Using global mailbox store limits\n"));
667
ldap_value_free (quota_setting_values);
669
quota_limit_values = ldap_get_values (gc->priv->ldap, msg, "mDBStorageQuota");
670
if (quota_limit_values) {
671
entry->quota_warn = atoi(quota_limit_values[0]);
672
E2K_GC_DEBUG_MSG(("GC: mDBStorageQuota %s\n", quota_limit_values[0]));
673
ldap_value_free (quota_limit_values);
676
quota_limit_values = ldap_get_values (gc->priv->ldap, msg, "mDBOverQuotaLimit");
677
if (quota_limit_values) {
678
entry->quota_nosend = atoi(quota_limit_values[0]);
679
E2K_GC_DEBUG_MSG(("GC: mDBOverQuotaLimit %s\n", quota_limit_values[0]));
680
ldap_value_free (quota_limit_values);
683
quota_limit_values = ldap_get_values (gc->priv->ldap, msg, "mDBOverHardQuotaLimit");
684
if (quota_limit_values) {
685
entry->quota_norecv = atoi(quota_limit_values[0]);
686
E2K_GC_DEBUG_MSG(("GC: mDBHardQuotaLimit %s\n", quota_limit_values[0]));
687
ldap_value_free (quota_limit_values);
692
get_account_control_values (E2kGlobalCatalog *gc, E2kOperation *op,
693
LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
697
values = ldap_get_values (gc->priv->ldap, msg, "userAccountControl");
699
entry->user_account_control = atoi(values[0]);
700
E2K_GC_DEBUG_MSG(("GC: userAccountControl %s\n", values[0]));
701
entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_ACCOUNT_CONTROL;
702
ldap_value_free (values);
708
* e2k_global_catalog_lookup:
709
* @gc: the global catalog
710
* @op: pointer to an #E2kOperation to use for cancellation
711
* @type: the type of information in @key
712
* @key: email address or DN to look up
713
* @flags: the information to look up
714
* @entry_p: pointer to a variable to return the entry in.
716
* Look up the indicated user in the global catalog and
717
* return their information in *@entry_p.
719
* Return value: the status of the lookup
721
E2kGlobalCatalogStatus
722
e2k_global_catalog_lookup (E2kGlobalCatalog *gc,
724
E2kGlobalCatalogLookupType type,
726
E2kGlobalCatalogLookupFlags flags,
727
E2kGlobalCatalogEntry **entry_p)
729
E2kGlobalCatalogEntry *entry;
731
E2kGlobalCatalogLookupFlags lookup_flags, need_flags = 0;
732
const gchar *base = NULL;
733
gchar *filter = NULL, *dn;
734
gint scope = LDAP_SCOPE_BASE, ldap_error;
735
E2kGlobalCatalogStatus status;
736
LDAPMessage *msg, *resp;
738
g_return_val_if_fail (E2K_IS_GLOBAL_CATALOG (gc), E2K_GLOBAL_CATALOG_ERROR);
739
g_return_val_if_fail (key != NULL, E2K_GLOBAL_CATALOG_ERROR);
741
g_mutex_lock (gc->priv->ldap_lock);
743
entry = g_hash_table_lookup (gc->priv->entry_cache, key);
745
entry = g_new0 (E2kGlobalCatalogEntry, 1);
747
attrs = g_ptr_array_new ();
749
if (!entry->display_name)
750
g_ptr_array_add (attrs, (guint8 *) "displayName");
752
g_ptr_array_add (attrs, (guint8 *) "mail");
753
if (flags & E2K_GLOBAL_CATALOG_LOOKUP_EMAIL)
754
need_flags |= E2K_GLOBAL_CATALOG_LOOKUP_EMAIL;
756
if (!entry->legacy_exchange_dn) {
757
g_ptr_array_add (attrs, (guint8 *) "legacyExchangeDN");
758
if (flags & E2K_GLOBAL_CATALOG_LOOKUP_LEGACY_EXCHANGE_DN)
759
need_flags |= E2K_GLOBAL_CATALOG_LOOKUP_LEGACY_EXCHANGE_DN;
762
lookup_flags = flags & ~entry->mask;
764
if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_SID) {
765
g_ptr_array_add (attrs, (guint8 *) "objectSid");
766
g_ptr_array_add (attrs, (guint8 *) "objectCategory");
767
need_flags |= E2K_GLOBAL_CATALOG_LOOKUP_SID;
769
if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_MAILBOX) {
770
g_ptr_array_add (attrs, (guint8 *) "mailNickname");
771
g_ptr_array_add (attrs, (guint8 *) "homeMTA");
772
need_flags |= E2K_GLOBAL_CATALOG_LOOKUP_MAILBOX;
774
if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_DELEGATES)
775
g_ptr_array_add (attrs, (guint8 *) "publicDelegates");
776
if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_DELEGATORS)
777
g_ptr_array_add (attrs, (guint8 *) "publicDelegatesBL");
778
if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_QUOTA) {
779
g_ptr_array_add (attrs, (guint8 *) "mDBUseDefaults");
780
g_ptr_array_add (attrs, (guint8 *) "mDBStorageQuota");
781
g_ptr_array_add (attrs, (guint8 *) "mDBOverQuotaLimit");
782
g_ptr_array_add (attrs, (guint8 *) "mDBOverHardQuotaLimit");
784
if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_ACCOUNT_CONTROL)
785
g_ptr_array_add (attrs, (guint8 *) "userAccountControl");
787
if (attrs->len == 0) {
788
E2K_GC_DEBUG_MSG(("\nGC: returning cached info for %s\n", key));
792
E2K_GC_DEBUG_MSG(("\nGC: looking up info for %s\n", key));
793
g_ptr_array_add (attrs, NULL);
796
case E2K_GLOBAL_CATALOG_LOOKUP_BY_EMAIL:
797
filter = g_strdup_printf ("(mail=%s)", key);
798
base = LDAP_ROOT_DSE;
799
scope = LDAP_SCOPE_SUBTREE;
802
case E2K_GLOBAL_CATALOG_LOOKUP_BY_DN:
805
scope = LDAP_SCOPE_BASE;
808
case E2K_GLOBAL_CATALOG_LOOKUP_BY_LEGACY_EXCHANGE_DN:
809
filter = g_strdup_printf ("(legacyExchangeDN=%s)", key);
810
base = LDAP_ROOT_DSE;
811
scope = LDAP_SCOPE_SUBTREE;
815
ldap_error = gc_search (gc, op, base, scope, filter,
816
(const gchar **)attrs->pdata, &msg);
817
if (ldap_error == LDAP_USER_CANCELLED) {
818
E2K_GC_DEBUG_MSG(("GC: ldap_search cancelled"));
819
status = E2K_GLOBAL_CATALOG_CANCELLED;
821
} else if (ldap_error == LDAP_INVALID_CREDENTIALS) {
822
E2K_GC_DEBUG_MSG(("GC: ldap_search auth failed"));
823
status = E2K_GLOBAL_CATALOG_AUTH_FAILED;
825
} else if (ldap_error != LDAP_SUCCESS) {
826
E2K_GC_DEBUG_MSG(("GC: ldap_search failed: 0x%02x\n\n", ldap_error));
827
status = E2K_GLOBAL_CATALOG_ERROR;
831
resp = ldap_first_entry (gc->priv->ldap, msg);
833
E2K_GC_DEBUG_MSG(("GC: no such user\n\n"));
834
status = E2K_GLOBAL_CATALOG_NO_SUCH_USER;
840
dn = ldap_get_dn (gc->priv->ldap, resp);
841
entry->dn = g_strdup (dn);
842
E2K_GC_DEBUG_MSG(("GC: dn = %s\n\n", dn));
844
g_ptr_array_add (gc->priv->entries, entry);
845
g_hash_table_insert (gc->priv->entry_cache,
849
get_sid_values (gc, op, resp, entry);
850
get_mail_values (gc, op, resp, entry);
851
get_delegation_values (gc, op, resp, entry);
852
get_quota_values (gc, op, resp, entry);
853
get_account_control_values (gc, op, resp, entry);
857
if (need_flags & ~entry->mask) {
858
E2K_GC_DEBUG_MSG(("GC: no data\n\n"));
859
status = E2K_GLOBAL_CATALOG_NO_DATA;
861
E2K_GC_DEBUG_MSG(("\n"));
862
status = E2K_GLOBAL_CATALOG_OK;
863
entry->mask |= lookup_flags;
869
g_ptr_array_free (attrs, TRUE);
871
if (status != E2K_GLOBAL_CATALOG_OK && !entry->dn)
874
g_mutex_unlock (gc->priv->ldap_lock);
878
struct async_lookup_data {
879
E2kGlobalCatalog *gc;
881
E2kGlobalCatalogLookupType type;
883
E2kGlobalCatalogLookupFlags flags;
884
E2kGlobalCatalogCallback callback;
887
E2kGlobalCatalogEntry *entry;
888
E2kGlobalCatalogStatus status;
892
idle_lookup_result (gpointer user_data)
894
struct async_lookup_data *ald = user_data;
896
ald->callback (ald->gc, ald->status, ald->entry, ald->user_data);
897
g_object_unref (ald->gc);
904
do_lookup_thread (gpointer user_data)
906
struct async_lookup_data *ald = user_data;
908
ald->status = e2k_global_catalog_lookup (ald->gc, ald->op, ald->type,
909
ald->key, ald->flags,
911
g_idle_add (idle_lookup_result, ald);
916
* e2k_global_catalog_async_lookup:
917
* @gc: the global catalog
918
* @op: pointer to an #E2kOperation to use for cancellation
919
* @type: the type of information in @key
920
* @key: email address or DN to look up
921
* @flags: the information to look up
922
* @callback: the callback to invoke after finding the user
923
* @user_data: data to pass to callback
925
* Asynchronously look up the indicated user in the global catalog and
926
* return the requested information to the callback.
929
e2k_global_catalog_async_lookup (E2kGlobalCatalog *gc,
931
E2kGlobalCatalogLookupType type,
933
E2kGlobalCatalogLookupFlags flags,
934
E2kGlobalCatalogCallback callback,
937
struct async_lookup_data *ald;
940
ald = g_new0 (struct async_lookup_data, 1);
941
ald->gc = g_object_ref (gc);
944
ald->key = g_strdup (key);
946
ald->callback = callback;
947
ald->user_data = user_data;
949
if (pthread_create (&pth, NULL, do_lookup_thread, ald) == -1) {
950
g_warning ("Could not create lookup thread\n");
951
ald->status = E2K_GLOBAL_CATALOG_ERROR;
952
g_idle_add (idle_lookup_result, ald);
957
lookup_controlling_ad_server (E2kGlobalCatalog *gc, E2kOperation *op,
960
gchar *hostname, **values, *ad_dn;
961
const gchar *attrs[2];
965
while (g_ascii_strncasecmp (dn, "DC=", 3) != 0) {
966
dn = strchr (dn, ',');
972
hostname = g_hash_table_lookup (gc->priv->server_cache, dn);
976
E2K_GC_DEBUG_MSG(("GC: Finding AD server for %s\n", dn));
978
attrs[0] = "masteredBy";
981
ldap_error = gc_search (gc, op, dn, LDAP_SCOPE_BASE, NULL, attrs, &resp);
982
if (ldap_error != LDAP_SUCCESS) {
983
E2K_GC_DEBUG_MSG(("GC: ldap_search failed: 0x%02x\n", ldap_error));
987
values = ldap_get_values (gc->priv->ldap, resp, "masteredBy");
990
E2K_GC_DEBUG_MSG(("GC: no known AD server\n\n"));
994
/* Skip over "CN=NTDS Settings," */
995
ad_dn = strchr (values[0], ',');
997
E2K_GC_DEBUG_MSG(("GC: bad dn %s\n\n", values[0]));
998
ldap_value_free (values);
1003
attrs[0] = "dNSHostName";
1006
ldap_error = gc_search (gc, op, ad_dn, LDAP_SCOPE_BASE, NULL, attrs, &resp);
1007
ldap_value_free (values);
1009
if (ldap_error != LDAP_SUCCESS) {
1010
E2K_GC_DEBUG_MSG(("GC: ldap_search failed: 0x%02x\n\n", ldap_error));
1014
values = ldap_get_values (gc->priv->ldap, resp, "dNSHostName");
1015
ldap_msgfree (resp);
1017
E2K_GC_DEBUG_MSG(("GC: entry has no dNSHostName\n\n"));
1021
hostname = g_strdup (values[0]);
1022
ldap_value_free (values);
1024
g_hash_table_insert (gc->priv->server_cache, g_strdup (dn), hostname);
1026
E2K_GC_DEBUG_MSG(("GC: %s\n", hostname));
1031
find_domain_dn (gchar *domain)
1033
GString *dn_value = g_string_new (NULL);
1035
gchar *sub_domain=NULL;
1037
sub_domain = strtok (domain, ".");
1038
while (sub_domain != NULL) {
1039
g_string_append (dn_value, "DC=");
1040
g_string_append (dn_value, sub_domain);
1041
g_string_append (dn_value, ",");
1042
sub_domain = strtok (NULL, ".");
1044
if (dn_value->str[0])
1045
dn = g_strndup (dn_value->str, strlen(dn_value->str) - 1);
1048
g_string_free (dn_value, TRUE);
1053
lookup_passwd_max_age (E2kGlobalCatalog *gc, E2kOperation *op)
1055
gchar **values = NULL, *filter = NULL, *val=NULL;
1056
const gchar *attrs[2];
1058
LDAPMessage *msg=NULL;
1059
gint ldap_error, msgid;
1063
attrs[0] = "maxPwdAge";
1066
filter = g_strdup("objectClass=domainDNS");
1068
dn = find_domain_dn (gc->domain);
1070
ldap_error = get_ldap_connection (gc, op, gc->priv->server, LDAP_PORT, &ldap);
1071
if (ldap_error != LDAP_SUCCESS) {
1072
E2K_GC_DEBUG_MSG(("GC: Establishing ldap connection failed : 0x%02x\n\n",
1077
ldap_error = ldap_search_ext (ldap, dn, LDAP_SCOPE_BASE, filter, (gchar **)attrs,
1078
FALSE, NULL, NULL, NULL, 0, &msgid);
1080
ldap_error = gc_ldap_result (ldap, op, msgid, &msg);
1082
E2K_GC_DEBUG_MSG(("GC: ldap_result failed: 0x%02x\n\n", ldap_error));
1087
E2K_GC_DEBUG_MSG(("GC: ldap_search failed:0x%02x \n\n", ldap_error));
1091
values = ldap_get_values (ldap, msg, "maxPwdAge");
1093
E2K_GC_DEBUG_MSG(("GC: couldn't retrieve maxPwdAge\n"));
1101
maxAge = strtod (val, NULL);
1104
/*g_hash_table_insert (gc->priv->server_cache, g_strdup (dn), hostname); FIXME? */
1106
E2K_GC_DEBUG_MSG(("GC: maxPwdAge = %f\n", maxAge));
1111
ldap_value_free (values);
1118
#if defined(G_OS_WIN32) || !defined(LDAP_TYPE_OR_VALUE_EXISTS)
1119
#define LDAP_TYPE_OR_VALUE_EXISTS 0x14
1121
static E2kGlobalCatalogStatus
1122
do_delegate_op (E2kGlobalCatalog *gc, E2kOperation *op, gint deleg_op,
1123
const gchar *self_dn, const gchar *delegate_dn)
1126
LDAPMod *mods[2], mod;
1127
const gchar *ad_server;
1129
gint ldap_error, msgid;
1131
g_return_val_if_fail (E2K_IS_GLOBAL_CATALOG (gc), E2K_GLOBAL_CATALOG_ERROR);
1132
g_return_val_if_fail (self_dn != NULL, E2K_GLOBAL_CATALOG_ERROR);
1133
g_return_val_if_fail (delegate_dn != NULL, E2K_GLOBAL_CATALOG_ERROR);
1135
ad_server = lookup_controlling_ad_server (gc, op, self_dn);
1137
if (e2k_operation_is_cancelled (op))
1138
return E2K_GLOBAL_CATALOG_CANCELLED;
1140
return E2K_GLOBAL_CATALOG_ERROR;
1143
ldap_error = get_ldap_connection (gc, op, ad_server, LDAP_PORT, &ldap);
1144
if (ldap_error == LDAP_USER_CANCELLED)
1145
return E2K_GLOBAL_CATALOG_CANCELLED;
1146
else if (ldap_error != LDAP_SUCCESS)
1147
return E2K_GLOBAL_CATALOG_ERROR;
1149
mod.mod_op = deleg_op;
1150
mod.mod_type = (gchar *) "publicDelegates";
1151
mod.mod_values = values;
1152
values[0] = (gchar *)delegate_dn;
1158
ldap_error = ldap_modify_ext (ldap, self_dn, mods, NULL, NULL, &msgid);
1159
if (ldap_error == LDAP_SUCCESS) {
1162
ldap_error = gc_ldap_result (ldap, op, msgid, &msg);
1163
if (ldap_error == LDAP_SUCCESS) {
1164
ldap_parse_result (ldap, msg, &ldap_error, NULL, NULL,
1170
switch (ldap_error) {
1172
E2K_GC_DEBUG_MSG(("\n"));
1173
return E2K_GLOBAL_CATALOG_OK;
1175
case LDAP_NO_SUCH_OBJECT:
1176
E2K_GC_DEBUG_MSG(("GC: no such user\n\n"));
1177
return E2K_GLOBAL_CATALOG_NO_SUCH_USER;
1179
case LDAP_NO_SUCH_ATTRIBUTE:
1180
E2K_GC_DEBUG_MSG(("GC: no such delegate\n\n"));
1181
return E2K_GLOBAL_CATALOG_NO_DATA;
1183
case LDAP_CONSTRAINT_VIOLATION:
1184
E2K_GC_DEBUG_MSG(("GC: bad delegate\n\n"));
1185
return E2K_GLOBAL_CATALOG_BAD_DATA;
1187
case LDAP_TYPE_OR_VALUE_EXISTS:
1188
E2K_GC_DEBUG_MSG(("GC: delegate already exists\n\n"));
1189
return E2K_GLOBAL_CATALOG_EXISTS;
1191
case LDAP_USER_CANCELLED:
1192
E2K_GC_DEBUG_MSG(("GC: cancelled\n\n"));
1193
return E2K_GLOBAL_CATALOG_CANCELLED;
1196
E2K_GC_DEBUG_MSG(("GC: ldap_modify failed: 0x%02x\n\n", ldap_error));
1197
return E2K_GLOBAL_CATALOG_ERROR;
1202
* e2k_global_catalog_add_delegate:
1203
* @gc: the global catalog
1204
* @op: pointer to an #E2kOperation to use for cancellation
1205
* @self_dn: Active Directory DN of the user to add a delegate to
1206
* @delegate_dn: Active Directory DN of the new delegate
1208
* Attempts to make @delegate_dn a delegate of @self_dn.
1210
* Return value: %E2K_GLOBAL_CATALOG_OK on success,
1211
* %E2K_GLOBAL_CATALOG_NO_SUCH_USER if @self_dn is invalid,
1212
* %E2K_GLOBAL_CATALOG_BAD_DATA if @delegate_dn is invalid,
1213
* %E2K_GLOBAL_CATALOG_EXISTS if @delegate_dn is already a delegate,
1214
* %E2K_GLOBAL_CATALOG_ERROR on other errors.
1216
E2kGlobalCatalogStatus
1217
e2k_global_catalog_add_delegate (E2kGlobalCatalog *gc,
1219
const gchar *self_dn,
1220
const gchar *delegate_dn)
1222
E2K_GC_DEBUG_MSG(("\nGC: adding %s as delegate for %s\n", delegate_dn, self_dn));
1224
return do_delegate_op (gc, op, LDAP_MOD_ADD, self_dn, delegate_dn);
1228
* e2k_global_catalog_remove_delegate:
1229
* @gc: the global catalog
1230
* @op: pointer to an #E2kOperation to use for cancellation
1231
* @self_dn: Active Directory DN of the user to remove a delegate from
1232
* @delegate_dn: Active Directory DN of the delegate to remove
1234
* Attempts to remove @delegate_dn as a delegate of @self_dn.
1236
* Return value: %E2K_GLOBAL_CATALOG_OK on success,
1237
* %E2K_GLOBAL_CATALOG_NO_SUCH_USER if @self_dn is invalid,
1238
* %E2K_GLOBAL_CATALOG_NO_DATA if @delegate_dn is not a delegate of @self_dn,
1239
* %E2K_GLOBAL_CATALOG_ERROR on other errors.
1241
E2kGlobalCatalogStatus
1242
e2k_global_catalog_remove_delegate (E2kGlobalCatalog *gc,
1244
const gchar *self_dn,
1245
const gchar *delegate_dn)
1247
E2K_GC_DEBUG_MSG(("\nGC: removing %s as delegate for %s\n", delegate_dn, self_dn));
1249
return do_delegate_op (gc, op, LDAP_MOD_DELETE, self_dn, delegate_dn);