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 char *server, *user, *nt_domain, *password;
54 #define PARENT_TYPE G_TYPE_OBJECT
55 static GObjectClass *parent_class = NULL;
57 static void finalize (GObject *);
58 static int get_gc_connection (E2kGlobalCatalog *gc, E2kOperation *op);
62 class_init (GObjectClass *object_class)
65 char *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...
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);
169 E2K_MAKE_TYPE (e2k_global_catalog, E2kGlobalCatalog, class_init, init, PARENT_TYPE)
172 gc_ldap_result (LDAP *ldap, E2kOperation *op,
173 int msgid, LDAPMessage **msg)
176 int status, ldap_error;
182 status = ldap_result (ldap, msgid, TRUE, &tv, msg);
184 ldap_get_option (ldap, LDAP_OPT_ERROR_NUMBER,
188 } while (status == 0 && !e2k_operation_is_cancelled (op));
190 if (e2k_operation_is_cancelled (op)) {
191 ldap_abandon (ldap, msgid);
192 return LDAP_USER_CANCELLED;
198 gc_search (E2kGlobalCatalog *gc, E2kOperation *op,
199 const char *base, int scope, const char *filter,
200 const char **attrs, LDAPMessage **msg)
202 int ldap_error, msgid, try;
204 for (try = 0; try < 2; try++) {
205 ldap_error = get_gc_connection (gc, op);
206 if (ldap_error != LDAP_SUCCESS)
208 ldap_error = ldap_search_ext (gc->priv->ldap, base, scope,
209 filter, (char **)attrs,
210 FALSE, NULL, NULL, NULL, 0,
212 if (ldap_error == LDAP_SERVER_DOWN)
214 else if (ldap_error != LDAP_SUCCESS)
217 ldap_error = gc_ldap_result (gc->priv->ldap, op, msgid, msg);
218 if (ldap_error == LDAP_SERVER_DOWN)
220 else if (ldap_error != LDAP_SUCCESS)
226 return LDAP_SERVER_DOWN;
229 #ifdef HAVE_LDAP_NTLM_BIND
231 ntlm_bind (E2kGlobalCatalog *gc, E2kOperation *op, LDAP *ldap)
234 int ldap_error, msgid, err;
235 char *nonce, *default_domain;
237 struct berval ldap_buf;
239 /* Create and send NTLM request */
240 ba = xntlm_negotiate ();
241 ldap_buf.bv_len = ba->len;
242 ldap_buf.bv_val = ba->data;
243 ldap_error = ldap_ntlm_bind (ldap, "NTLM", LDAP_AUTH_NTLM_REQUEST,
244 &ldap_buf, NULL, NULL, &msgid);
245 g_byte_array_free (ba, TRUE);
246 if (ldap_error != LDAP_SUCCESS) {
247 E2K_GC_DEBUG_MSG(("GC: Failure sending first NTLM bind message: 0x%02x\n", ldap_error));
251 /* Extract challenge */
252 ldap_error = gc_ldap_result (ldap, op, msgid, &msg);
253 if (ldap_error != LDAP_SUCCESS) {
254 E2K_GC_DEBUG_MSG(("GC: Could not parse first NTLM bind response\n"));
257 ldap_error = ldap_parse_ntlm_bind_result (ldap, msg, &ldap_buf);
259 if (ldap_error != LDAP_SUCCESS) {
260 E2K_GC_DEBUG_MSG(("GC: Could not parse NTLM bind response: 0x%02x\n", ldap_error));
264 if (!xntlm_parse_challenge (ldap_buf.bv_val, ldap_buf.bv_len,
265 &nonce, &default_domain,
267 E2K_GC_DEBUG_MSG(("GC: Could not find nonce in NTLM bind response\n"));
268 ber_memfree (ldap_buf.bv_val);
270 return LDAP_DECODING_ERROR;
272 ber_memfree (ldap_buf.bv_val);
274 /* Create and send response */
275 ba = xntlm_authenticate (nonce, gc->priv->nt_domain ? gc->priv->nt_domain : default_domain,
276 gc->priv->user, gc->priv->password, NULL);
277 ldap_buf.bv_len = ba->len;
278 ldap_buf.bv_val = ba->data;
279 ldap_error = ldap_ntlm_bind (ldap, "NTLM", LDAP_AUTH_NTLM_RESPONSE,
280 &ldap_buf, NULL, NULL, &msgid);
281 g_byte_array_free (ba, TRUE);
283 g_free (default_domain);
284 if (ldap_error != LDAP_SUCCESS) {
285 E2K_GC_DEBUG_MSG(("GC: Failure sending second NTLM bind message: 0x%02x\n", ldap_error));
289 /* And get the final result */
290 ldap_error = gc_ldap_result (ldap, op, msgid, &msg);
291 if (ldap_error != LDAP_SUCCESS) {
292 E2K_GC_DEBUG_MSG(("GC: Could not parse second NTLM bind response\n"));
295 ldap_error = ldap_parse_result (ldap, msg, &err, NULL, NULL,
297 if (ldap_error != LDAP_SUCCESS) {
298 E2K_GC_DEBUG_MSG(("GC: Could not parse second NTLM bind response: 0x%02x\n", ldap_error));
307 connect_ldap (E2kGlobalCatalog *gc, E2kOperation *op, LDAP *ldap)
310 #ifndef HAVE_LDAP_NTLM_BIND
313 SEC_WINNT_AUTH_IDENTITY_W auth;
318 #ifdef HAVE_LDAP_NTLM_BIND
319 ldap_error = ntlm_bind (gc, op, ldap);
321 nt_name = gc->priv->nt_domain ?
322 g_strdup_printf ("%s\\%s", gc->priv->nt_domain, gc->priv->user) :
323 g_strdup (gc->priv->user);
325 ldap_error = ldap_simple_bind_s (ldap, nt_name, gc->priv->password);
327 auth.User = g_utf8_to_utf16 (gc->priv->user, -1, NULL, NULL, NULL);
328 auth.UserLength = wcslen (auth.User);
329 auth.Domain = gc->priv->nt_domain ?
330 g_utf8_to_utf16 (gc->priv->nt_domain, -1, NULL, NULL, NULL) :
331 g_utf8_to_utf16 ("", -1, NULL, NULL, NULL);
332 auth.DomainLength = wcslen (auth.Domain);
333 auth.Password = g_utf8_to_utf16 (gc->priv->password, -1, NULL, NULL, NULL);
334 auth.PasswordLength = wcslen (auth.Password);
335 auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
336 ldap_error = ldap_bind_s (ldap, nt_name, &auth, LDAP_AUTH_NTLM);
337 g_free (auth.Password);
338 g_free (auth.Domain);
343 if (ldap_error != LDAP_SUCCESS)
344 g_warning ("LDAP authentication failed (0x%02x)", ldap_error);
346 E2K_GC_DEBUG_MSG(("GC: connected\n\n"));
352 get_ldap_connection (E2kGlobalCatalog *gc, E2kOperation *op,
353 const char *server, int port,
356 int ldap_opt, ldap_error;
358 E2K_GC_DEBUG_MSG(("\nGC: Connecting to ldap://%s:%d/\n", server, port));
360 *ldap = ldap_init (server, port);
362 E2K_GC_DEBUG_MSG(("GC: failed\n\n"));
363 g_warning ("Could not connect to ldap://%s:%d/",
365 return LDAP_SERVER_DOWN;
369 ldap_opt = LDAP_DEREF_ALWAYS;
370 ldap_set_option (*ldap, LDAP_OPT_DEREF, &ldap_opt);
371 ldap_opt = gc->response_limit;
372 ldap_set_option (*ldap, LDAP_OPT_SIZELIMIT, &ldap_opt);
373 ldap_opt = LDAP_VERSION3;
374 ldap_set_option (*ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_opt);
376 ldap_error = connect_ldap (gc, op, *ldap);
377 if (ldap_error != LDAP_SUCCESS) {
385 get_gc_connection (E2kGlobalCatalog *gc, E2kOperation *op)
389 if (gc->priv->ldap) {
390 ldap_get_option (gc->priv->ldap, LDAP_OPT_ERROR_NUMBER, &err);
391 if (err != LDAP_SERVER_DOWN)
394 return connect_ldap (gc, op, gc->priv->ldap);
396 return get_ldap_connection (gc, op,
397 gc->priv->server, 3268,
403 * e2k_global_catalog_get_ldap:
404 * @gc: the global catalog
405 * @op: pointer to an initialized #E2kOperation to use for cancellation
407 * Returns a new LDAP handle. The caller must ldap_unbind() it when it
410 * Return value: an LDAP handle, or %NULL if it can't connect
413 e2k_global_catalog_get_ldap (E2kGlobalCatalog *gc, E2kOperation *op)
417 g_return_val_if_fail (E2K_IS_GLOBAL_CATALOG (gc), NULL);
419 get_ldap_connection (gc, op, gc->priv->server, 3268, &ldap);
424 * e2k_global_catalog_new:
425 * @server: the GC server name
426 * @response_limit: the maximum number of responses to return from a search
427 * @user: username to authenticate with
428 * @domain: NT domain of @user, or %NULL to autodetect.
429 * @password: password to authenticate with
431 * Create an object for communicating with the Windows Global Catalog
434 * Return value: the new E2kGlobalCatalog. (This call will always succeed.
435 * If the passed-in data is bad, it will fail on a later call.)
438 e2k_global_catalog_new (const char *server, int response_limit,
439 const char *user, const char *domain,
440 const char *password)
442 E2kGlobalCatalog *gc;
444 gc = g_object_new (E2K_TYPE_GLOBAL_CATALOG, NULL);
445 gc->priv->server = g_strdup (server);
446 gc->priv->user = g_strdup (user);
447 gc->priv->nt_domain = g_strdup (domain);
448 gc->priv->password = g_strdup (password);
449 gc->response_limit = response_limit;
455 lookup_mta (E2kGlobalCatalog *gc, E2kOperation *op, const char *mta_dn)
457 char *hostname, **values;
458 const char *attrs[2];
462 /* Skip over "CN=Microsoft MTA," */
463 mta_dn = strchr (mta_dn, ',');
468 hostname = g_hash_table_lookup (gc->priv->server_cache, mta_dn);
472 E2K_GC_DEBUG_MSG(("GC: Finding hostname for %s\n", mta_dn));
474 attrs[0] = "networkAddress";
477 ldap_error = gc_search (gc, op, mta_dn, LDAP_SCOPE_BASE,
479 if (ldap_error != LDAP_SUCCESS) {
480 E2K_GC_DEBUG_MSG(("GC: lookup failed (0x%02x)\n", ldap_error));
484 values = ldap_get_values (gc->priv->ldap, resp, "networkAddress");
487 E2K_GC_DEBUG_MSG(("GC: entry has no networkAddress\n"));
492 for (i = 0; values[i]; i++) {
493 if (strstr (values[i], "_tcp")) {
494 hostname = strchr (values[i], ':');
499 E2K_GC_DEBUG_MSG(("GC: host is not availble by TCP?\n"));
500 ldap_value_free (values);
504 hostname = g_strdup (hostname + 1);
505 g_hash_table_insert (gc->priv->server_cache, g_strdup (mta_dn), hostname);
506 ldap_value_free (values);
508 E2K_GC_DEBUG_MSG(("GC: %s\n", hostname));
514 get_sid_values (E2kGlobalCatalog *gc, E2kOperation *op,
515 LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
518 struct berval **bsid_values;
521 values = ldap_get_values (gc->priv->ldap, msg, "displayName");
523 E2K_GC_DEBUG_MSG(("GC: displayName %s\n", values[0]));
524 entry->display_name = g_strdup (values[0]);
525 ldap_value_free (values);
528 bsid_values = ldap_get_values_len (gc->priv->ldap, msg, "objectSid");
531 if (bsid_values[0]->bv_len < 2 ||
532 bsid_values[0]->bv_len != E2K_SID_BINARY_SID_LEN (bsid_values[0]->bv_val)) {
533 E2K_GC_DEBUG_MSG(("GC: invalid SID\n"));
537 values = ldap_get_values (gc->priv->ldap, msg, "objectCategory");
538 if (values && values[0] && !g_ascii_strncasecmp (values[0], "CN=Group", 8))
539 type = E2K_SID_TYPE_GROUP;
540 else if (values && values[0] && !g_ascii_strncasecmp (values[0], "CN=Foreign", 10))
541 type = E2K_SID_TYPE_WELL_KNOWN_GROUP;
543 type = E2K_SID_TYPE_USER;
545 ldap_value_free (values);
547 entry->sid = e2k_sid_new_from_binary_sid (
548 type, bsid_values[0]->bv_val, entry->display_name);
549 entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_SID;
551 ldap_value_free_len (bsid_values);
555 get_mail_values (E2kGlobalCatalog *gc, E2kOperation *op,
556 LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
558 char **values, **mtavalues;
560 values = ldap_get_values (gc->priv->ldap, msg, "mail");
562 E2K_GC_DEBUG_MSG(("GC: mail %s\n", values[0]));
563 entry->email = g_strdup (values[0]);
564 g_hash_table_insert (gc->priv->entry_cache,
565 entry->email, entry);
566 entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_EMAIL;
567 ldap_value_free (values);
570 values = ldap_get_values (gc->priv->ldap, msg, "mailNickname");
571 mtavalues = ldap_get_values (gc->priv->ldap, msg, "homeMTA");
572 if (values && mtavalues) {
573 E2K_GC_DEBUG_MSG(("GC: mailNickname %s\n", values[0]));
574 E2K_GC_DEBUG_MSG(("GC: homeMTA %s\n", mtavalues[0]));
575 entry->exchange_server = (char *)lookup_mta (gc, op, mtavalues[0]);
576 ldap_value_free (mtavalues);
577 if (entry->exchange_server)
578 entry->mailbox = g_strdup (values[0]);
579 ldap_value_free (values);
580 entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_MAILBOX;
583 values = ldap_get_values (gc->priv->ldap, msg, "legacyExchangeDN");
585 E2K_GC_DEBUG_MSG(("GC: legacyExchangeDN %s\n", values[0]));
586 entry->legacy_exchange_dn = g_strdup (values[0]);
587 g_hash_table_insert (gc->priv->entry_cache,
588 entry->legacy_exchange_dn,
590 entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_LEGACY_EXCHANGE_DN;
591 ldap_value_free (values);
596 get_delegation_values (E2kGlobalCatalog *gc, E2kOperation *op,
597 LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
602 values = ldap_get_values (gc->priv->ldap, msg, "publicDelegates");
604 E2K_GC_DEBUG_MSG(("GC: publicDelegates\n"));
605 entry->delegates = g_ptr_array_new ();
606 for (i = 0; values[i]; i++) {
607 E2K_GC_DEBUG_MSG(("GC: %s\n", values[i]));
608 g_ptr_array_add (entry->delegates,
609 g_strdup (values[i]));
611 entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_DELEGATES;
612 ldap_value_free (values);
614 values = ldap_get_values (gc->priv->ldap, msg, "publicDelegatesBL");
616 E2K_GC_DEBUG_MSG(("GC: publicDelegatesBL\n"));
617 entry->delegators = g_ptr_array_new ();
618 for (i = 0; values[i]; i++) {
619 E2K_GC_DEBUG_MSG(("GC: %s\n", values[i]));
620 g_ptr_array_add (entry->delegators,
621 g_strdup (values[i]));
623 entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_DELEGATORS;
624 ldap_value_free (values);
629 get_quota_values (E2kGlobalCatalog *gc, E2kOperation *op,
630 LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
632 char **quota_setting_values, **quota_limit_values;
634 /* Check if mailbox store default values are used */
635 quota_setting_values = ldap_get_values (gc->priv->ldap, msg, "mDBUseDefaults");
636 if (!quota_setting_values) {
637 entry->quota_warn = entry->quota_nosend = entry->quota_norecv = 0;
641 entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_QUOTA;
642 E2K_GC_DEBUG_MSG(("GC: mDBUseDefaults %s\n", quota_setting_values[0]));
644 if (!strcmp (quota_setting_values[0], "TRUE")) {
645 /* use global mailbox store settings */
646 E2K_GC_DEBUG_MSG(("GC: Using global mailbox store limits\n"));
648 ldap_value_free (quota_setting_values);
650 quota_limit_values = ldap_get_values (gc->priv->ldap, msg, "mDBStorageQuota");
651 if (quota_limit_values) {
652 entry->quota_warn = atoi(quota_limit_values[0]);
653 E2K_GC_DEBUG_MSG(("GC: mDBStorageQuota %s\n", quota_limit_values[0]));
654 ldap_value_free (quota_limit_values);
657 quota_limit_values = ldap_get_values (gc->priv->ldap, msg, "mDBOverQuotaLimit");
658 if (quota_limit_values) {
659 entry->quota_nosend = atoi(quota_limit_values[0]);
660 E2K_GC_DEBUG_MSG(("GC: mDBOverQuotaLimit %s\n", quota_limit_values[0]));
661 ldap_value_free (quota_limit_values);
664 quota_limit_values = ldap_get_values (gc->priv->ldap, msg, "mDBOverHardQuotaLimit");
665 if (quota_limit_values) {
666 entry->quota_norecv = atoi(quota_limit_values[0]);
667 E2K_GC_DEBUG_MSG(("GC: mDBHardQuotaLimit %s\n", quota_limit_values[0]));
668 ldap_value_free (quota_limit_values);
673 get_account_control_values (E2kGlobalCatalog *gc, E2kOperation *op,
674 LDAPMessage *msg, E2kGlobalCatalogEntry *entry)
678 values = ldap_get_values (gc->priv->ldap, msg, "userAccountControl");
680 entry->user_account_control = atoi(values[0]);
681 E2K_GC_DEBUG_MSG(("GC: userAccountControl %s\n", values[0]));
682 entry->mask |= E2K_GLOBAL_CATALOG_LOOKUP_ACCOUNT_CONTROL;
683 ldap_value_free (values);
689 * e2k_global_catalog_lookup:
690 * @gc: the global catalog
691 * @op: pointer to an #E2kOperation to use for cancellation
692 * @type: the type of information in @key
693 * @key: email address or DN to look up
694 * @flags: the information to look up
695 * @entry_p: pointer to a variable to return the entry in.
697 * Look up the indicated user in the global catalog and
698 * return their information in *@entry_p.
700 * Return value: the status of the lookup
702 E2kGlobalCatalogStatus
703 e2k_global_catalog_lookup (E2kGlobalCatalog *gc,
705 E2kGlobalCatalogLookupType type,
707 E2kGlobalCatalogLookupFlags flags,
708 E2kGlobalCatalogEntry **entry_p)
710 E2kGlobalCatalogEntry *entry;
712 E2kGlobalCatalogLookupFlags lookup_flags, need_flags = 0;
713 const char *base = NULL;
714 char *filter = NULL, *dn;
715 int scope = LDAP_SCOPE_BASE, ldap_error;
716 E2kGlobalCatalogStatus status;
717 LDAPMessage *msg, *resp;
719 g_return_val_if_fail (E2K_IS_GLOBAL_CATALOG (gc), E2K_GLOBAL_CATALOG_ERROR);
720 g_return_val_if_fail (key != NULL, E2K_GLOBAL_CATALOG_ERROR);
722 g_mutex_lock (gc->priv->ldap_lock);
724 entry = g_hash_table_lookup (gc->priv->entry_cache, key);
726 entry = g_new0 (E2kGlobalCatalogEntry, 1);
728 attrs = g_ptr_array_new ();
730 if (!entry->display_name)
731 g_ptr_array_add (attrs, "displayName");
733 g_ptr_array_add (attrs, "mail");
734 if (flags & E2K_GLOBAL_CATALOG_LOOKUP_EMAIL)
735 need_flags |= E2K_GLOBAL_CATALOG_LOOKUP_EMAIL;
737 if (!entry->legacy_exchange_dn) {
738 g_ptr_array_add (attrs, "legacyExchangeDN");
739 if (flags & E2K_GLOBAL_CATALOG_LOOKUP_LEGACY_EXCHANGE_DN)
740 need_flags |= E2K_GLOBAL_CATALOG_LOOKUP_LEGACY_EXCHANGE_DN;
743 lookup_flags = flags & ~entry->mask;
745 if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_SID) {
746 g_ptr_array_add (attrs, "objectSid");
747 g_ptr_array_add (attrs, "objectCategory");
748 need_flags |= E2K_GLOBAL_CATALOG_LOOKUP_SID;
750 if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_MAILBOX) {
751 g_ptr_array_add (attrs, "mailNickname");
752 g_ptr_array_add (attrs, "homeMTA");
753 need_flags |= E2K_GLOBAL_CATALOG_LOOKUP_MAILBOX;
755 if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_DELEGATES)
756 g_ptr_array_add (attrs, "publicDelegates");
757 if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_DELEGATORS)
758 g_ptr_array_add (attrs, "publicDelegatesBL");
759 if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_QUOTA) {
760 g_ptr_array_add (attrs, "mDBUseDefaults");
761 g_ptr_array_add (attrs, "mDBStorageQuota");
762 g_ptr_array_add (attrs, "mDBOverQuotaLimit");
763 g_ptr_array_add (attrs, "mDBOverHardQuotaLimit");
765 if (lookup_flags & E2K_GLOBAL_CATALOG_LOOKUP_ACCOUNT_CONTROL)
766 g_ptr_array_add (attrs, "userAccountControl");
768 if (attrs->len == 0) {
769 E2K_GC_DEBUG_MSG(("\nGC: returning cached info for %s\n", key));
773 E2K_GC_DEBUG_MSG(("\nGC: looking up info for %s\n", key));
774 g_ptr_array_add (attrs, NULL);
777 case E2K_GLOBAL_CATALOG_LOOKUP_BY_EMAIL:
778 filter = g_strdup_printf ("(mail=%s)", key);
779 base = LDAP_ROOT_DSE;
780 scope = LDAP_SCOPE_SUBTREE;
783 case E2K_GLOBAL_CATALOG_LOOKUP_BY_DN:
786 scope = LDAP_SCOPE_BASE;
789 case E2K_GLOBAL_CATALOG_LOOKUP_BY_LEGACY_EXCHANGE_DN:
790 filter = g_strdup_printf ("(legacyExchangeDN=%s)", key);
791 base = LDAP_ROOT_DSE;
792 scope = LDAP_SCOPE_SUBTREE;
796 ldap_error = gc_search (gc, op, base, scope, filter,
797 (const char **)attrs->pdata, &msg);
798 if (ldap_error == LDAP_USER_CANCELLED) {
799 E2K_GC_DEBUG_MSG(("GC: ldap_search cancelled"));
800 status = E2K_GLOBAL_CATALOG_CANCELLED;
802 } else if (ldap_error == LDAP_INVALID_CREDENTIALS) {
803 E2K_GC_DEBUG_MSG(("GC: ldap_search auth failed"));
804 status = E2K_GLOBAL_CATALOG_AUTH_FAILED;
806 } else if (ldap_error != LDAP_SUCCESS) {
807 E2K_GC_DEBUG_MSG(("GC: ldap_search failed: 0x%02x\n\n", ldap_error));
808 status = E2K_GLOBAL_CATALOG_ERROR;
812 resp = ldap_first_entry (gc->priv->ldap, msg);
814 E2K_GC_DEBUG_MSG(("GC: no such user\n\n"));
815 status = E2K_GLOBAL_CATALOG_NO_SUCH_USER;
821 dn = ldap_get_dn (gc->priv->ldap, resp);
822 entry->dn = g_strdup (dn);
823 E2K_GC_DEBUG_MSG(("GC: dn = %s\n\n", dn));
825 g_ptr_array_add (gc->priv->entries, entry);
826 g_hash_table_insert (gc->priv->entry_cache,
830 get_sid_values (gc, op, resp, entry);
831 get_mail_values (gc, op, resp, entry);
832 get_delegation_values (gc, op, resp, entry);
833 get_quota_values (gc, op, resp, entry);
834 get_account_control_values (gc, op, resp, entry);
838 if (need_flags & ~entry->mask) {
839 E2K_GC_DEBUG_MSG(("GC: no data\n\n"));
840 status = E2K_GLOBAL_CATALOG_NO_DATA;
842 E2K_GC_DEBUG_MSG(("\n"));
843 status = E2K_GLOBAL_CATALOG_OK;
844 entry->mask |= lookup_flags;
850 g_ptr_array_free (attrs, TRUE);
852 if (status != E2K_GLOBAL_CATALOG_OK && !entry->dn)
855 g_mutex_unlock (gc->priv->ldap_lock);
860 struct async_lookup_data {
861 E2kGlobalCatalog *gc;
863 E2kGlobalCatalogLookupType type;
865 E2kGlobalCatalogLookupFlags flags;
866 E2kGlobalCatalogCallback callback;
869 E2kGlobalCatalogEntry *entry;
870 E2kGlobalCatalogStatus status;
874 idle_lookup_result (gpointer user_data)
876 struct async_lookup_data *ald = user_data;
878 ald->callback (ald->gc, ald->status, ald->entry, ald->user_data);
879 g_object_unref (ald->gc);
886 do_lookup_thread (void *user_data)
888 struct async_lookup_data *ald = user_data;
890 ald->status = e2k_global_catalog_lookup (ald->gc, ald->op, ald->type,
891 ald->key, ald->flags,
893 g_idle_add (idle_lookup_result, ald);
898 * e2k_global_catalog_async_lookup:
899 * @gc: the global catalog
900 * @op: pointer to an #E2kOperation to use for cancellation
901 * @type: the type of information in @key
902 * @key: email address or DN to look up
903 * @flags: the information to look up
904 * @callback: the callback to invoke after finding the user
905 * @user_data: data to pass to callback
907 * Asynchronously look up the indicated user in the global catalog and
908 * return the requested information to the callback.
911 e2k_global_catalog_async_lookup (E2kGlobalCatalog *gc,
913 E2kGlobalCatalogLookupType type,
915 E2kGlobalCatalogLookupFlags flags,
916 E2kGlobalCatalogCallback callback,
919 struct async_lookup_data *ald;
922 ald = g_new0 (struct async_lookup_data, 1);
923 ald->gc = g_object_ref (gc);
926 ald->key = g_strdup (key);
928 ald->callback = callback;
929 ald->user_data = user_data;
931 if (pthread_create (&pth, NULL, do_lookup_thread, ald) == -1) {
932 g_warning ("Could not create lookup thread\n");
933 ald->status = E2K_GLOBAL_CATALOG_ERROR;
934 g_idle_add (idle_lookup_result, ald);
939 lookup_controlling_ad_server (E2kGlobalCatalog *gc, E2kOperation *op,
942 char *hostname, **values, *ad_dn;
943 const char *attrs[2];
947 while (g_ascii_strncasecmp (dn, "DC=", 3) != 0) {
948 dn = strchr (dn, ',');
954 hostname = g_hash_table_lookup (gc->priv->server_cache, dn);
958 E2K_GC_DEBUG_MSG(("GC: Finding AD server for %s\n", dn));
960 attrs[0] = "masteredBy";
963 ldap_error = gc_search (gc, op, dn, LDAP_SCOPE_BASE, NULL, attrs, &resp);
964 if (ldap_error != LDAP_SUCCESS) {
965 E2K_GC_DEBUG_MSG(("GC: ldap_search failed: 0x%02x\n", ldap_error));
969 values = ldap_get_values (gc->priv->ldap, resp, "masteredBy");
972 E2K_GC_DEBUG_MSG(("GC: no known AD server\n\n"));
976 /* Skip over "CN=NTDS Settings," */
977 ad_dn = strchr (values[0], ',');
979 E2K_GC_DEBUG_MSG(("GC: bad dn %s\n\n", values[0]));
980 ldap_value_free (values);
985 attrs[0] = "dNSHostName";
988 ldap_error = gc_search (gc, op, ad_dn, LDAP_SCOPE_BASE, NULL, attrs, &resp);
989 ldap_value_free (values);
991 if (ldap_error != LDAP_SUCCESS) {
992 E2K_GC_DEBUG_MSG(("GC: ldap_search failed: 0x%02x\n\n", ldap_error));
996 values = ldap_get_values (gc->priv->ldap, resp, "dNSHostName");
999 E2K_GC_DEBUG_MSG(("GC: entry has no dNSHostName\n\n"));
1003 hostname = g_strdup (values[0]);
1004 ldap_value_free (values);
1006 g_hash_table_insert (gc->priv->server_cache, g_strdup (dn), hostname);
1008 E2K_GC_DEBUG_MSG(("GC: %s\n", hostname));
1013 find_domain_dn (char *domain)
1015 GString *dn_value = g_string_new (NULL);
1017 char *sub_domain=NULL;
1019 sub_domain = strtok (domain, ".");
1020 while (sub_domain != NULL) {
1021 g_string_append (dn_value, "DC=");
1022 g_string_append (dn_value, sub_domain);
1023 g_string_append (dn_value, ",");
1024 sub_domain = strtok (NULL, ".");
1026 if (dn_value->str[0])
1027 dn = g_strndup (dn_value->str, strlen(dn_value->str) - 1);
1030 g_string_free (dn_value, TRUE);
1035 lookup_passwd_max_age (E2kGlobalCatalog *gc, E2kOperation *op)
1037 char **values = NULL, *filter = NULL, *val=NULL;
1038 const char *attrs[2];
1040 LDAPMessage *msg=NULL;
1041 int ldap_error, msgid;
1045 attrs[0] = "maxPwdAge";
1048 filter = g_strdup("objectClass=domainDNS");
1050 dn = find_domain_dn (gc->domain);
1052 ldap_error = get_ldap_connection (gc, op, gc->priv->server, LDAP_PORT, &ldap);
1053 if (ldap_error != LDAP_SUCCESS) {
1054 E2K_GC_DEBUG_MSG(("GC: Establishing ldap connection failed : 0x%02x\n\n",
1059 ldap_error = ldap_search_ext (ldap, dn, LDAP_SCOPE_BASE, filter, (char **)attrs,
1060 FALSE, NULL, NULL, NULL, 0, &msgid);
1062 ldap_error = gc_ldap_result (ldap, op, msgid, &msg);
1064 E2K_GC_DEBUG_MSG(("GC: ldap_result failed: 0x%02x\n\n", ldap_error));
1069 E2K_GC_DEBUG_MSG(("GC: ldap_search failed:0x%02x \n\n", ldap_error));
1073 values = ldap_get_values (ldap, msg, "maxPwdAge");
1075 E2K_GC_DEBUG_MSG(("GC: couldn't retrieve maxPwdAge\n"));
1083 maxAge = strtod (val, NULL);
1086 //g_hash_table_insert (gc->priv->server_cache, g_strdup (dn), hostname); FIXME?
1088 E2K_GC_DEBUG_MSG(("GC: maxPwdAge = %f\n", maxAge));
1093 ldap_value_free (values);
1100 static E2kGlobalCatalogStatus
1101 do_delegate_op (E2kGlobalCatalog *gc, E2kOperation *op, int deleg_op,
1102 const char *self_dn, const char *delegate_dn)
1105 LDAPMod *mods[2], mod;
1106 const char *ad_server;
1108 int ldap_error, msgid;
1110 g_return_val_if_fail (E2K_IS_GLOBAL_CATALOG (gc), E2K_GLOBAL_CATALOG_ERROR);
1111 g_return_val_if_fail (self_dn != NULL, E2K_GLOBAL_CATALOG_ERROR);
1112 g_return_val_if_fail (delegate_dn != NULL, E2K_GLOBAL_CATALOG_ERROR);
1114 ad_server = lookup_controlling_ad_server (gc, op, self_dn);
1116 if (e2k_operation_is_cancelled (op))
1117 return E2K_GLOBAL_CATALOG_CANCELLED;
1119 return E2K_GLOBAL_CATALOG_ERROR;
1122 ldap_error = get_ldap_connection (gc, op, ad_server, LDAP_PORT, &ldap);
1123 if (ldap_error == LDAP_USER_CANCELLED)
1124 return E2K_GLOBAL_CATALOG_CANCELLED;
1125 else if (ldap_error != LDAP_SUCCESS)
1126 return E2K_GLOBAL_CATALOG_ERROR;
1128 mod.mod_op = deleg_op;
1129 mod.mod_type = "publicDelegates";
1130 mod.mod_values = values;
1131 values[0] = (char *)delegate_dn;
1137 ldap_error = ldap_modify_ext (ldap, self_dn, mods, NULL, NULL, &msgid);
1138 if (ldap_error == LDAP_SUCCESS) {
1141 ldap_error = gc_ldap_result (ldap, op, msgid, &msg);
1142 if (ldap_error == LDAP_SUCCESS) {
1143 ldap_parse_result (ldap, msg, &ldap_error, NULL, NULL,
1149 switch (ldap_error) {
1151 E2K_GC_DEBUG_MSG(("\n"));
1152 return E2K_GLOBAL_CATALOG_OK;
1154 case LDAP_NO_SUCH_OBJECT:
1155 E2K_GC_DEBUG_MSG(("GC: no such user\n\n"));
1156 return E2K_GLOBAL_CATALOG_NO_SUCH_USER;
1158 case LDAP_NO_SUCH_ATTRIBUTE:
1159 E2K_GC_DEBUG_MSG(("GC: no such delegate\n\n"));
1160 return E2K_GLOBAL_CATALOG_NO_DATA;
1162 case LDAP_CONSTRAINT_VIOLATION:
1163 E2K_GC_DEBUG_MSG(("GC: bad delegate\n\n"));
1164 return E2K_GLOBAL_CATALOG_BAD_DATA;
1166 case LDAP_TYPE_OR_VALUE_EXISTS:
1167 E2K_GC_DEBUG_MSG(("GC: delegate already exists\n\n"));
1168 return E2K_GLOBAL_CATALOG_EXISTS;
1170 case LDAP_USER_CANCELLED:
1171 E2K_GC_DEBUG_MSG(("GC: cancelled\n\n"));
1172 return E2K_GLOBAL_CATALOG_CANCELLED;
1175 E2K_GC_DEBUG_MSG(("GC: ldap_modify failed: 0x%02x\n\n", ldap_error));
1176 return E2K_GLOBAL_CATALOG_ERROR;
1181 * e2k_global_catalog_add_delegate:
1182 * @gc: the global catalog
1183 * @op: pointer to an #E2kOperation to use for cancellation
1184 * @self_dn: Active Directory DN of the user to add a delegate to
1185 * @delegate_dn: Active Directory DN of the new delegate
1187 * Attempts to make @delegate_dn a delegate of @self_dn.
1189 * Return value: %E2K_GLOBAL_CATALOG_OK on success,
1190 * %E2K_GLOBAL_CATALOG_NO_SUCH_USER if @self_dn is invalid,
1191 * %E2K_GLOBAL_CATALOG_BAD_DATA if @delegate_dn is invalid,
1192 * %E2K_GLOBAL_CATALOG_EXISTS if @delegate_dn is already a delegate,
1193 * %E2K_GLOBAL_CATALOG_ERROR on other errors.
1195 E2kGlobalCatalogStatus
1196 e2k_global_catalog_add_delegate (E2kGlobalCatalog *gc,
1198 const char *self_dn,
1199 const char *delegate_dn)
1201 E2K_GC_DEBUG_MSG(("\nGC: adding %s as delegate for %s\n", delegate_dn, self_dn));
1203 return do_delegate_op (gc, op, LDAP_MOD_ADD, self_dn, delegate_dn);
1207 * e2k_global_catalog_remove_delegate:
1208 * @gc: the global catalog
1209 * @op: pointer to an #E2kOperation to use for cancellation
1210 * @self_dn: Active Directory DN of the user to remove a delegate from
1211 * @delegate_dn: Active Directory DN of the delegate to remove
1213 * Attempts to remove @delegate_dn as a delegate of @self_dn.
1215 * Return value: %E2K_GLOBAL_CATALOG_OK on success,
1216 * %E2K_GLOBAL_CATALOG_NO_SUCH_USER if @self_dn is invalid,
1217 * %E2K_GLOBAL_CATALOG_NO_DATA if @delegate_dn is not a delegate of @self_dn,
1218 * %E2K_GLOBAL_CATALOG_ERROR on other errors.
1220 E2kGlobalCatalogStatus
1221 e2k_global_catalog_remove_delegate (E2kGlobalCatalog *gc,
1223 const char *self_dn,
1224 const char *delegate_dn)
1226 E2K_GC_DEBUG_MSG(("\nGC: removing %s as delegate for %s\n", delegate_dn, self_dn));
1228 return do_delegate_op (gc, op, LDAP_MOD_DELETE, self_dn, delegate_dn);