From f946e2d02d19204da9c7b32b5281d7cfea52bd3f Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Wed, 22 Aug 2012 17:57:55 +0200 Subject: [PATCH 1/2] Integrate kerberos discovery * Discover everything about a kerberos domain in one place rather than duplicating implementation in different providers. * This lets us consolidate logic, and coordinate between the different types of kerberos domains. https://bugs.freedesktop.org/show_bug.cgi?id=53956 --- dbus/realm-dbus-constants.h | 1 - service/Makefile.am | 2 +- service/realm-ad-discover.c | 455 --------------------------------- service/realm-ad-discover.h | 35 --- service/realm-discovery.c | 10 + service/realm-discovery.h | 4 + service/realm-ipa-discover.c | 130 +--------- service/realm-ipa-discover.h | 5 +- service/realm-kerberos-discover.c | 515 ++++++++++++++++++++++++++++++++++++++ service/realm-kerberos-discover.h | 35 +++ service/realm-samba-provider.c | 22 +- service/realm-samba.c | 1 - service/realm-sssd-ad-provider.c | 28 ++- service/realm-sssd-ad.c | 1 - service/realm-sssd-ipa-provider.c | 20 +- 15 files changed, 621 insertions(+), 643 deletions(-) delete mode 100644 service/realm-ad-discover.c delete mode 100644 service/realm-ad-discover.h create mode 100644 service/realm-kerberos-discover.c create mode 100644 service/realm-kerberos-discover.h diff --git a/dbus/realm-dbus-constants.h b/dbus/realm-dbus-constants.h index eee45b1..77ee618 100644 --- a/dbus/realm-dbus-constants.h +++ b/dbus/realm-dbus-constants.h @@ -45,7 +45,6 @@ G_BEGIN_DECLS #define REALM_DBUS_ERROR_NOT_CONFIGURED "org.freedesktop.realmd.Error.NotConfigured" #define REALM_DBUS_ERROR_AUTH_FAILED "org.freedesktop.realmd.Error.AuthenticationFailed" -#define REALM_DBUS_DISCOVERY_TYPE "type" #define REALM_DBUS_DISCOVERY_DOMAIN "domain" #define REALM_DBUS_DISCOVERY_KDCS "kerberos-kdcs" #define REALM_DBUS_DISCOVERY_REALM "kerberos-realm" diff --git a/service/Makefile.am b/service/Makefile.am index 22a8a10..128d7ce 100644 --- a/service/Makefile.am +++ b/service/Makefile.am @@ -17,7 +17,6 @@ polkit_policy_DATA = $(polkit_policy_in_files:.policy.in=.policy) @INTLTOOL_POLICY_RULE@ realmd_SOURCES = \ - realm-ad-discover.c realm-ad-discover.h \ realm-all-provider.c realm-all-provider.h \ realm-command.c realm-command.h \ realm-daemon.c realm-daemon.h \ @@ -28,6 +27,7 @@ realmd_SOURCES = \ realm-ini-config.c realm-ini-config.h \ realm-ipa-discover.c realm-ipa-discover.h \ realm-kerberos.c realm-kerberos.h \ + realm-kerberos-discover.c realm-kerberos-discover.h \ realm-login-name.c realm-login-name.h \ realm-network.c realm-network.h \ realm-packages.c realm-packages.h \ diff --git a/service/realm-ad-discover.c b/service/realm-ad-discover.c deleted file mode 100644 index 66fcb59..0000000 --- a/service/realm-ad-discover.c +++ /dev/null @@ -1,455 +0,0 @@ -/* realmd -- Realm configuration service - * - * Copyright 2012 Red Hat Inc - * - * This program 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 2 of the licence or (at - * your option) any later version. - * - * See the included COPYING file for more information. - * - * Author: Stef Walter - */ - -#include "config.h" - -#include "realm-ad-discover.h" -#include "realm-command.h" -#include "realm-dbus-constants.h" -#include "realm-diagnostics.h" -#include "realm-discovery.h" -#include "realm-errors.h" -#include "realm-network.h" - -#include - -typedef struct { - gchar *string; - GDBusMethodInvocation *invocation; -} Key; - -typedef struct _Callback { - GAsyncReadyCallback function; - gpointer user_data; - struct _Callback *next; -} Callback; - -typedef struct { - GObject parent; - Key key; - gchar *domain; - GList *servers; - gboolean found_kerberos; - gboolean finished_kerberos; - gboolean found_msdcs; - gboolean finished_msdcs; - gboolean completed; - GError *error; - Callback *callback; -} RealmAdDiscover; - -typedef struct { - GObjectClass parent; -} RealmAdDiscoverClass; - -#define REALM_TYPE_AD_DISCOVER (realm_ad_discover_get_type ()) -#define REALM_AD_DISCOVER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), REALM_TYPE_AD_DISCOVER, RealmAdDiscover)) -#define REALM_IS_AD_DISCOVER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), REALM_TYPE_AD_DISCOVER)) - -static GHashTable *discover_cache = NULL; - -GType realm_ad_discover_get_type (void) G_GNUC_CONST; - -void realm_ad_discover_async_result_init (GAsyncResultIface *iface); - -G_DEFINE_TYPE_WITH_CODE (RealmAdDiscover, realm_ad_discover, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, realm_ad_discover_async_result_init); -); - -static void -realm_ad_discover_init (RealmAdDiscover *self) -{ - -} - -static void -realm_ad_discover_finalize (GObject *obj) -{ - RealmAdDiscover *self = REALM_AD_DISCOVER (obj); - - g_object_unref (self->key.invocation); - g_free (self->key.string); - g_free (self->domain); - if (self->servers) - g_list_free_full (self->servers, (GDestroyNotify)g_srv_target_free); - g_clear_error (&self->error); - g_assert (self->callback == NULL); - - G_OBJECT_CLASS (realm_ad_discover_parent_class)->finalize (obj); -} - -static void -realm_ad_discover_class_init (RealmAdDiscoverClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = realm_ad_discover_finalize; -} - -static GObject * -realm_ad_discover_get_source_object (GAsyncResult *result) -{ - return g_object_ref (result); -} - -static gpointer -realm_ad_discover_get_user_data (GAsyncResult *result) -{ - /* What does this do? */ - g_return_val_if_reached (NULL); -} - -void -realm_ad_discover_async_result_init (GAsyncResultIface *iface) -{ - iface->get_source_object = realm_ad_discover_get_source_object; - iface->get_user_data = realm_ad_discover_get_user_data; -} - - - -typedef struct { - Callback *clinger; -} DiscoverClosure; - -static void -ad_discover_complete (RealmAdDiscover *self) -{ - Callback *call, *next; - - g_object_ref (self); - - self->completed = TRUE; - call = self->callback; - self->callback = NULL; - - while (call != NULL) { - next = call->next; - if (call->function) - (call->function) (NULL, G_ASYNC_RESULT (self), call->user_data); - g_slice_free (Callback, call); - call = next; - } - - g_object_unref (self); -} - -static void -maybe_complete_discover (RealmAdDiscover *self) -{ - GDBusMethodInvocation *invocation = self->key.invocation; - - if (!self->finished_kerberos || !self->finished_msdcs) - return; - - if (self->found_kerberos && self->found_msdcs) - realm_diagnostics_info (invocation, "Found AD style DNS records on domain"); - else - realm_diagnostics_info (invocation, "Couldn't find AD style DNS records on domain"); - - ad_discover_complete (self); -} - -static void -on_resolve_kerberos (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - RealmAdDiscover *self = REALM_AD_DISCOVER (user_data); - GDBusMethodInvocation *invocation = self->key.invocation; - GError *error = NULL; - GString *info; - GList *l; - - self->servers = g_resolver_lookup_service_finish (G_RESOLVER (source), - result, &error); - - /* We don't treat 'host not found' as an error */ - if (g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND)) - g_clear_error (&error); - - if (error == NULL) { - info = g_string_new (""); - - for (l = self->servers; l != NULL; l = g_list_next (l)) { - self->found_kerberos = TRUE; - g_string_append_printf (info, "%s:%d\n", g_srv_target_get_hostname (l->data), - (int)g_srv_target_get_port (l->data)); - } - - if (self->found_kerberos) - realm_diagnostics_info (invocation, "%s", info->str); - else - realm_diagnostics_info (invocation, "No kerberos SRV records"); - - g_string_free (info, TRUE); - - } else { - realm_diagnostics_error (invocation, error, "Couldn't lookup SRV records for domain"); - g_clear_error (&self->error); - self->error = error; - } - - self->finished_kerberos = TRUE; - maybe_complete_discover (self); - g_object_unref (self); -} - -static void -on_resolve_msdcs (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - RealmAdDiscover *self = REALM_AD_DISCOVER (user_data); - GDBusMethodInvocation *invocation = self->key.invocation; - GResolver *resolver = G_RESOLVER (source); - GError *error = NULL; - GList *records; - - records = g_resolver_lookup_records_finish (resolver, result, &error); - if (error == NULL || g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND)) { - self->found_msdcs = (records != NULL); - g_list_free_full (records, (GDestroyNotify)g_variant_unref); - - } else { - realm_diagnostics_error (invocation, error, "Failure to lookup domain MSDCS records"); - g_clear_error (&self->error); - self->error = error; - } - - self->finished_msdcs = TRUE; - maybe_complete_discover (self); - g_object_unref (self); -} - -static void -ad_discover_domain_begin (RealmAdDiscover *self) -{ - GDBusMethodInvocation *invocation = self->key.invocation; - GResolver *resolver; - gchar *msdcs; - - g_assert (self->domain != NULL); - - realm_diagnostics_info (invocation, - "Searching for kerberos SRV records for domain: _kerberos._udp.%s", - self->domain); - - resolver = g_resolver_get_default (); - g_resolver_lookup_service_async (resolver, "kerberos", "udp", self->domain, NULL, - on_resolve_kerberos, g_object_ref (self)); - - /* Active Directory DNS zones have this subzone */ - msdcs = g_strdup_printf ("dc._msdcs.%s", self->domain); - - realm_diagnostics_info (invocation, - "Searching for MSDCS SRV records on domain: _kerberos._tcp.%s", - msdcs); - - g_resolver_lookup_service_async (resolver, "kerberos", "tcp", msdcs, NULL, - on_resolve_msdcs, g_object_ref (self)); - - g_free (msdcs); - - g_object_unref (resolver); -} - -static void -on_get_dhcp_domain (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - RealmAdDiscover *self = REALM_AD_DISCOVER (user_data); - GDBusMethodInvocation *invocation = self->key.invocation; - GError *error = NULL; - - self->domain = realm_network_get_dhcp_domain_finish (result, &error); - if (error != NULL) { - realm_diagnostics_error (invocation, error, "Failure to lookup DHCP domain"); - g_error_free (error); - } - - if (self->domain) { - realm_diagnostics_info (invocation, "Discovering for DHCP domain: %s", self->domain); - ad_discover_domain_begin (self); - } else { - realm_diagnostics_info (invocation, "No DHCP domain available"); - ad_discover_complete (self); - } - - g_object_unref (self); -} - -static inline guint -str_hash0 (gconstpointer p) -{ - return p == NULL ? 0 : g_str_hash (p); -} - -static guint -discover_key_hash (gconstpointer p) -{ - const Key *key = p; - - return str_hash0 (key->string) ^ - str_hash0 (realm_diagnostics_get_operation_id (key->invocation)) ^ - str_hash0 (g_dbus_method_invocation_get_sender (key->invocation)); -} - -static gboolean -discover_key_equal (gconstpointer v1, - gconstpointer v2) -{ - const Key *k1 = v1; - const Key *k2 = v2; - - return g_strcmp0 (k1->string, k2->string) == 0 && - g_strcmp0 (realm_diagnostics_get_operation_id (k1->invocation), - realm_diagnostics_get_operation_id (k2->invocation)) == 0 && - g_strcmp0 (g_dbus_method_invocation_get_sender (k1->invocation), - g_dbus_method_invocation_get_sender (k2->invocation)) == 0; -} - -static gboolean -on_timeout_remove_cache (gpointer user_data) -{ - Key *key = user_data; - - if (discover_cache != NULL) { - g_hash_table_remove (discover_cache, key); - if (g_hash_table_size (discover_cache) == 0) { - g_hash_table_destroy (discover_cache); - discover_cache = NULL; - } - } - - return FALSE; -} - -static gboolean -on_idle_complete (gpointer user_data) -{ - RealmAdDiscover *self = REALM_AD_DISCOVER (user_data); - g_assert (self->completed); - ad_discover_complete (self); - return FALSE; -} - -void -realm_ad_discover_async (const gchar *string, - GDBusMethodInvocation *invocation, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GDBusConnection *connection; - RealmAdDiscover *self; - Callback *call; - gchar *domain; - Key key; - - g_return_if_fail (string != NULL); - g_return_if_fail (invocation == NULL || G_IS_DBUS_METHOD_INVOCATION (invocation)); - - key.string = (gchar *)string; - key.invocation = invocation; - - if (!discover_cache) { - discover_cache = g_hash_table_new_full (discover_key_hash, discover_key_equal, - NULL, g_object_unref); - } - - self = g_hash_table_lookup (discover_cache, &key); - - if (self == NULL) { - self = g_object_new (REALM_TYPE_AD_DISCOVER, NULL); - self->key.string = g_strdup (string); - self->key.invocation = g_object_ref (invocation); - - if (g_str_equal (string, "")) { - connection = g_dbus_method_invocation_get_connection (invocation); - realm_diagnostics_info (invocation, "Looking up our DHCP domain"); - realm_network_get_dhcp_domain_async (connection, on_get_dhcp_domain, - g_object_ref (self)); - - } else { - domain = g_ascii_strdown (string, -1); - g_strstrip (domain); - self->domain = domain; - ad_discover_domain_begin (self); - } - - g_hash_table_insert (discover_cache, &self->key, self); - g_timeout_add_seconds (5, on_timeout_remove_cache, &self->key); - g_assert (!self->completed); - - } else if (self->completed) { - g_idle_add_full (G_PRIORITY_DEFAULT, on_idle_complete, - g_object_ref (self), g_object_unref); - } - - call = g_slice_new0 (Callback); - call->function = callback; - call->user_data = user_data; - call->next = self->callback; - self->callback = call; -} - -gchar * -realm_ad_discover_finish (GAsyncResult *result, - GHashTable **discovery, - GError **error) -{ - RealmAdDiscover *self; - gchar *realm; - - g_return_val_if_fail (REALM_IS_AD_DISCOVER (result), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - self = REALM_AD_DISCOVER (result); - - /* A failure */ - if (self->error) { - if (error) - *error = g_error_copy (self->error); - return NULL; - } - - /* Didn't find a valid domain */ - if (!self->found_kerberos || !self->found_msdcs) - return NULL; - - realm = g_ascii_strup (self->domain, -1); - - if (discovery) { - *discovery = realm_discovery_new (); - - /* The domain */ - realm_discovery_add_string (*discovery, REALM_DBUS_DISCOVERY_DOMAIN, - self->domain); - - /* The realm */ - realm_discovery_add_string (*discovery, REALM_DBUS_DISCOVERY_REALM, realm); - - /* The servers */ - realm_discovery_add_srv_targets (*discovery, REALM_DBUS_DISCOVERY_KDCS, - self->servers); - - /* The type */ - realm_discovery_add_string (*discovery, REALM_DBUS_DISCOVERY_TYPE, - "kerberos-ad"); - } - - realm_diagnostics_info (self->key.invocation, "Successfully discovered realm: %s", realm); - return realm; -} diff --git a/service/realm-ad-discover.h b/service/realm-ad-discover.h deleted file mode 100644 index 8760cbc..0000000 --- a/service/realm-ad-discover.h +++ /dev/null @@ -1,35 +0,0 @@ -/* realmd -- Realm configuration service - * - * Copyright 2012 Red Hat Inc - * - * This program 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 2 of the licence or (at - * your option) any later version. - * - * See the included COPYING file for more information. - * - * Author: Stef Walter - */ - -#include "config.h" - -#ifndef __REALM_AD_DISCOVER_H__ -#define __REALM_AD_DISCOVER_H__ - -#include - -G_BEGIN_DECLS - -void realm_ad_discover_async (const gchar *string, - GDBusMethodInvocation *invocation, - GAsyncReadyCallback callback, - gpointer user_data); - -gchar * realm_ad_discover_finish (GAsyncResult *result, - GHashTable **discovery, - GError **error); - -G_END_DECLS - -#endif /* __REALM_AD_DISCOVER_H__ */ diff --git a/service/realm-discovery.c b/service/realm-discovery.c index 86f87f8..ffd6be7 100644 --- a/service/realm-discovery.c +++ b/service/realm-discovery.c @@ -55,6 +55,16 @@ realm_discovery_get_string (GHashTable *discovery, return g_variant_get_string (variant, NULL); } +gboolean +realm_discovery_has_string (GHashTable *discovery, + const gchar *type, + const gchar *value) +{ + const gchar *has; + has = realm_discovery_get_string (discovery, type); + return has != NULL && g_str_equal (has, value); +} + void realm_discovery_add_variant (GHashTable *discovery, const gchar *type, diff --git a/service/realm-discovery.h b/service/realm-discovery.h index 2ef80e2..08ff912 100644 --- a/service/realm-discovery.h +++ b/service/realm-discovery.h @@ -30,6 +30,10 @@ void realm_discovery_add_string (GHashTable *discovery, const gchar * realm_discovery_get_string (GHashTable *discovery, const gchar *type); +gboolean realm_discovery_has_string (GHashTable *discovery, + const gchar *type, + const gchar *value); + void realm_discovery_add_variant (GHashTable *discovery, const gchar *type, GVariant *value); diff --git a/service/realm-ipa-discover.c b/service/realm-ipa-discover.c index cca2452..941dc96 100644 --- a/service/realm-ipa-discover.c +++ b/service/realm-ipa-discover.c @@ -28,7 +28,6 @@ typedef struct { GObject parent; - gchar *domain; GDBusMethodInvocation *invocation; GList *services; gboolean completed; @@ -78,7 +77,6 @@ realm_ipa_discover_finalize (GObject *obj) RealmIpaDiscover *self = REALM_IPA_DISCOVER (obj); g_object_unref (self->invocation); - g_free (self->domain); g_list_free_full (self->services, (GDestroyNotify)g_srv_target_free); g_clear_error (&self->error); @@ -592,89 +590,15 @@ ipa_discover_step (RealmIpaDiscover *self) g_object_unref (client); } -static void -on_resolve_kerberos (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - RealmIpaDiscover *self = REALM_IPA_DISCOVER (user_data); - GDBusMethodInvocation *invocation = self->invocation; - GError *error = NULL; - GList *l; - - self->services = g_resolver_lookup_service_finish (G_RESOLVER (source), result, &error); - - /* We don't treat 'host not found' as an error */ - if (g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND)) - g_clear_error (&error); - - if (error == NULL) { - for (l = self->services; l != NULL; l = g_list_next (l)) - g_queue_push_tail (&self->remaining_services, l->data); - ipa_discover_step (self); - - } else { - realm_diagnostics_error (invocation, error, "Couldn't lookup SRV records for domain"); - ipa_discover_take_error (self, "Couldn't lookup SRV records for domain", error); - ipa_discover_complete (self); - } - - g_object_unref (self); -} - -static void -ipa_discover_begin (RealmIpaDiscover *self) -{ - GDBusMethodInvocation *invocation = self->invocation; - GResolver *resolver; - - g_assert (self->domain != NULL); - - realm_diagnostics_info (invocation, - "Searching for kerberos SRV records for domain: _kerberos._udp.%s", - self->domain); - - resolver = g_resolver_get_default (); - g_resolver_lookup_service_async (resolver, "kerberos", "udp", self->domain, NULL, - on_resolve_kerberos, g_object_ref (self)); - - g_object_unref (resolver); -} - -static void -on_get_dhcp_domain (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - RealmIpaDiscover *self = REALM_IPA_DISCOVER (user_data); - GDBusMethodInvocation *invocation = self->invocation; - GError *error = NULL; - - self->domain = realm_network_get_dhcp_domain_finish (result, &error); - if (error != NULL) { - realm_diagnostics_error (invocation, error, "Failure to lookup DHCP domain"); - g_error_free (error); - } else if (self->domain) { - realm_diagnostics_info (invocation, "Discovering for DHCP domain: %s", self->domain); - ipa_discover_begin (self); - } else { - realm_diagnostics_info (invocation, "No DHCP domain available"); - ipa_discover_complete (self); - } - - g_object_unref (self); -} - void -realm_ipa_discover_async (const gchar *string, +realm_ipa_discover_async (GList *kdcs, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, gpointer user_data) { - GDBusConnection *connection; RealmIpaDiscover *self; + GList *l; - g_return_if_fail (string != NULL); g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation)); self = g_object_new (REALM_TYPE_IPA_DISCOVER, NULL); @@ -682,31 +606,22 @@ realm_ipa_discover_async (const gchar *string, self->callback = callback; self->user_data = user_data; - if (g_str_equal (string, "")) { - connection = g_dbus_method_invocation_get_connection (invocation); - realm_diagnostics_info (invocation, "Looking up our DHCP domain"); - realm_network_get_dhcp_domain_async (connection, on_get_dhcp_domain, - g_object_ref (self)); + for (l = kdcs; l != NULL; l = g_list_next (l)) + g_queue_push_tail (&self->remaining_services, l->data); - } else { - self->domain = g_ascii_strdown (string, -1); - g_strstrip (self->domain); - ipa_discover_begin (self); - } + ipa_discover_step (self); /* self is released in ipa_discover_complete */ } -gchar * +gboolean realm_ipa_discover_finish (GAsyncResult *result, - GHashTable **discovery, GError **error) { RealmIpaDiscover *self; - gchar *realm; - g_return_val_if_fail (REALM_IS_IPA_DISCOVER (result), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); + g_return_val_if_fail (REALM_IS_IPA_DISCOVER (result), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); self = REALM_IPA_DISCOVER (result); @@ -714,33 +629,8 @@ realm_ipa_discover_finish (GAsyncResult *result, if (self->error) { if (error) *error = g_error_copy (self->error); - return NULL; - } - - if (!self->found_ipa) - return NULL; - - realm = g_ascii_strup (self->domain, -1); - - if (discovery) { - *discovery = realm_discovery_new (); - - /* The domain */ - realm_discovery_add_string (*discovery, REALM_DBUS_DISCOVERY_DOMAIN, - self->domain); - - /* The realm */ - realm_discovery_add_string (*discovery, REALM_DBUS_DISCOVERY_REALM, realm); - - /* The servers */ - realm_discovery_add_srv_targets (*discovery, REALM_DBUS_DISCOVERY_KDCS, - self->services); - - /* The type */ - realm_discovery_add_string (*discovery, REALM_DBUS_DISCOVERY_TYPE, - "kerberos-ad"); + return FALSE; } - realm_diagnostics_info (self->invocation, "Successfully discovered realm: %s", realm); - return realm; + return self->found_ipa; } diff --git a/service/realm-ipa-discover.h b/service/realm-ipa-discover.h index 2ad0288..8930696 100644 --- a/service/realm-ipa-discover.h +++ b/service/realm-ipa-discover.h @@ -21,13 +21,12 @@ G_BEGIN_DECLS -void realm_ipa_discover_async (const gchar *string, +void realm_ipa_discover_async (GList *kdcs, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, gpointer user_data); -gchar * realm_ipa_discover_finish (GAsyncResult *result, - GHashTable **discovery, +gboolean realm_ipa_discover_finish (GAsyncResult *result, GError **error); G_END_DECLS diff --git a/service/realm-kerberos-discover.c b/service/realm-kerberos-discover.c new file mode 100644 index 0000000..1e6fdb5 --- /dev/null +++ b/service/realm-kerberos-discover.c @@ -0,0 +1,515 @@ +/* realmd -- Realm configuration service + * + * Copyright 2012 Red Hat Inc + * + * This program 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 2 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: Stef Walter + */ + +#include "config.h" + +#include "realm-command.h" +#include "realm-dbus-constants.h" +#include "realm-diagnostics.h" +#include "realm-discovery.h" +#include "realm-errors.h" +#include "realm-ipa-discover.h" +#include "realm-kerberos-discover.h" +#include "realm-network.h" + +#include + +typedef struct { + gchar *string; + GDBusMethodInvocation *invocation; +} Key; + +typedef struct _Callback { + GAsyncReadyCallback function; + gpointer user_data; + struct _Callback *next; +} Callback; + +typedef struct { + GObject parent; + Key key; + gchar *domain; + GList *servers; + gboolean found_kerberos; + gboolean finished_kerberos; + gboolean found_msdcs; + gboolean finished_msdcs; + gboolean found_ipa; + gboolean finished_ipa; + gboolean completed; + GError *error; + Callback *callback; +} RealmKerberosDiscover; + +typedef struct { + GObjectClass parent; +} RealmKerberosDiscoverClass; + +#define REALM_TYPE_KERBEROS_DISCOVER (realm_kerberos_discover_get_type ()) +#define REALM_KERBEROS_DISCOVER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), REALM_TYPE_KERBEROS_DISCOVER, RealmKerberosDiscover)) +#define REALM_IS_KERBEROS_DISCOVER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), REALM_TYPE_KERBEROS_DISCOVER)) + +static GHashTable *discover_cache = NULL; + +GType realm_kerberos_discover_get_type (void) G_GNUC_CONST; + +void realm_kerberos_discover_async_result_init (GAsyncResultIface *iface); + +G_DEFINE_TYPE_WITH_CODE (RealmKerberosDiscover, realm_kerberos_discover, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, realm_kerberos_discover_async_result_init); +); + +static void +realm_kerberos_discover_init (RealmKerberosDiscover *self) +{ + +} + +static void +realm_kerberos_discover_finalize (GObject *obj) +{ + RealmKerberosDiscover *self = REALM_KERBEROS_DISCOVER (obj); + + g_object_unref (self->key.invocation); + g_free (self->key.string); + g_free (self->domain); + if (self->servers) + g_list_free_full (self->servers, (GDestroyNotify)g_srv_target_free); + g_clear_error (&self->error); + g_assert (self->callback == NULL); + + G_OBJECT_CLASS (realm_kerberos_discover_parent_class)->finalize (obj); +} + +static void +realm_kerberos_discover_class_init (RealmKerberosDiscoverClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = realm_kerberos_discover_finalize; +} + +static GObject * +realm_kerberos_discover_get_source_object (GAsyncResult *result) +{ + return g_object_ref (result); +} + +static gpointer +realm_kerberos_discover_get_user_data (GAsyncResult *result) +{ + /* What does this do? */ + g_return_val_if_reached (NULL); +} + +void +realm_kerberos_discover_async_result_init (GAsyncResultIface *iface) +{ + iface->get_source_object = realm_kerberos_discover_get_source_object; + iface->get_user_data = realm_kerberos_discover_get_user_data; +} + +typedef struct { + Callback *clinger; +} DiscoverClosure; + +static void +kerberos_discover_complete (RealmKerberosDiscover *self) +{ + Callback *call, *next; + + g_object_ref (self); + + g_assert (!self->completed); + self->completed = TRUE; + call = self->callback; + self->callback = NULL; + + if (self->error == NULL && self->found_kerberos) + realm_diagnostics_info (self->key.invocation, "Successfully discovered: %s", self->domain); + + while (call != NULL) { + next = call->next; + if (call->function) + (call->function) (NULL, G_ASYNC_RESULT (self), call->user_data); + g_slice_free (Callback, call); + call = next; + } + + g_object_unref (self); +} + +static void +maybe_complete_discover (RealmKerberosDiscover *self) +{ + GDBusMethodInvocation *invocation = self->key.invocation; + gboolean complete; + + /* If discovered either AD or IPA successfully, then complete */ + complete = (self->finished_kerberos && + (self->found_msdcs || self->found_ipa || + (self->finished_msdcs && self->finished_ipa))); + + if (!complete) + return; + + if (self->found_kerberos) { + realm_diagnostics_info (invocation, "Found kerberos DNS records for: %s", self->domain); + if (self->found_msdcs) + realm_diagnostics_info (invocation, "Found AD style DNS records for: %s", self->domain); + else if (self->found_ipa) + realm_diagnostics_info (invocation, "Found IPA style certificate for: %s", self->domain); + } else { + realm_diagnostics_info (invocation, "Couldn't find kerberos DNS records for: %s", self->domain); + } + + kerberos_discover_complete (self); +} + +static void +on_discover_ipa (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + RealmKerberosDiscover *self = REALM_KERBEROS_DISCOVER (user_data); + GError *error = NULL; + + if (self->completed) { + g_object_unref (self); + return; + } + + self->found_ipa = realm_ipa_discover_finish (result, &error); + if (error != NULL) { + g_clear_error (&self->error); + self->error = error; + } + + self->finished_ipa = TRUE; + maybe_complete_discover (self); + g_object_unref (self); +} + +static void +on_resolve_kerberos (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + RealmKerberosDiscover *self = REALM_KERBEROS_DISCOVER (user_data); + GDBusMethodInvocation *invocation = self->key.invocation; + GError *error = NULL; + GString *info; + GList *l; + + if (self->completed) { + g_object_unref (self); + return; + } + + self->servers = g_resolver_lookup_service_finish (G_RESOLVER (source), result, &error); + + /* We don't treat 'host not found' as an error */ + if (g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND)) + g_clear_error (&error); + + if (error == NULL) { + info = g_string_new (""); + + for (l = self->servers; l != NULL; l = g_list_next (l)) { + self->found_kerberos = TRUE; + g_string_append_printf (info, "%s:%d ", g_srv_target_get_hostname (l->data), + (int)g_srv_target_get_port (l->data)); + } + + if (self->found_kerberos) + realm_diagnostics_info (invocation, "%s", info->str); + + g_string_free (info, TRUE); + + /* Unless sure domain is AD, start IPA discovery */ + if (!self->found_msdcs) { + realm_ipa_discover_async (self->servers, invocation, + on_discover_ipa, g_object_ref (self)); + } + + } else { + realm_diagnostics_error (invocation, error, "Couldn't lookup SRV records for domain"); + g_clear_error (&self->error); + self->error = error; + } + + self->finished_kerberos = TRUE; + maybe_complete_discover (self); + g_object_unref (self); +} + +static void +on_resolve_msdcs (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + RealmKerberosDiscover *self = REALM_KERBEROS_DISCOVER (user_data); + GDBusMethodInvocation *invocation = self->key.invocation; + GResolver *resolver = G_RESOLVER (source); + GError *error = NULL; + GList *records; + + if (self->completed) { + g_object_unref (self); + return; + } + + records = g_resolver_lookup_records_finish (resolver, result, &error); + if (error == NULL || g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND)) { + self->found_msdcs = (records != NULL); + g_list_free_full (records, (GDestroyNotify)g_variant_unref); + + } else { + realm_diagnostics_error (invocation, error, "Failure to lookup domain MSDCS records"); + g_clear_error (&self->error); + self->error = error; + } + + self->finished_msdcs = TRUE; + maybe_complete_discover (self); + g_object_unref (self); +} + +static void +kerberos_discover_domain_begin (RealmKerberosDiscover *self) +{ + GDBusMethodInvocation *invocation = self->key.invocation; + GResolver *resolver; + gchar *msdcs; + + g_assert (self->domain != NULL); + + realm_diagnostics_info (invocation, + "Searching for kerberos SRV records for domain: _kerberos._udp.%s", + self->domain); + + resolver = g_resolver_get_default (); + g_resolver_lookup_service_async (resolver, "kerberos", "udp", self->domain, NULL, + on_resolve_kerberos, g_object_ref (self)); + + /* Active Directory DNS zones have this subzone */ + msdcs = g_strdup_printf ("dc._msdcs.%s", self->domain); + + realm_diagnostics_info (invocation, + "Searching for MSDCS SRV records on domain: _kerberos._tcp.%s", + msdcs); + + g_resolver_lookup_service_async (resolver, "kerberos", "tcp", msdcs, NULL, + on_resolve_msdcs, g_object_ref (self)); + + g_free (msdcs); + + g_object_unref (resolver); +} + +static void +on_get_dhcp_domain (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + RealmKerberosDiscover *self = REALM_KERBEROS_DISCOVER (user_data); + GDBusMethodInvocation *invocation = self->key.invocation; + GError *error = NULL; + + self->domain = realm_network_get_dhcp_domain_finish (result, &error); + if (error != NULL) { + realm_diagnostics_error (invocation, error, "Failure to lookup DHCP domain"); + g_error_free (error); + } + + if (self->domain) { + realm_diagnostics_info (invocation, "Discovering for DHCP domain: %s", self->domain); + kerberos_discover_domain_begin (self); + } else { + realm_diagnostics_info (invocation, "No DHCP domain available"); + kerberos_discover_complete (self); + } + + g_object_unref (self); +} + +static inline guint +str_hash0 (gconstpointer p) +{ + return p == NULL ? 0 : g_str_hash (p); +} + +static guint +discover_key_hash (gconstpointer p) +{ + const Key *key = p; + + return str_hash0 (key->string) ^ + str_hash0 (realm_diagnostics_get_operation_id (key->invocation)) ^ + str_hash0 (g_dbus_method_invocation_get_sender (key->invocation)); +} + +static gboolean +discover_key_equal (gconstpointer v1, + gconstpointer v2) +{ + const Key *k1 = v1; + const Key *k2 = v2; + + return g_strcmp0 (k1->string, k2->string) == 0 && + g_strcmp0 (realm_diagnostics_get_operation_id (k1->invocation), + realm_diagnostics_get_operation_id (k2->invocation)) == 0 && + g_strcmp0 (g_dbus_method_invocation_get_sender (k1->invocation), + g_dbus_method_invocation_get_sender (k2->invocation)) == 0; +} + +static gboolean +on_timeout_remove_cache (gpointer user_data) +{ + Key *key = user_data; + + if (discover_cache != NULL) { + g_hash_table_remove (discover_cache, key); + if (g_hash_table_size (discover_cache) == 0) { + g_hash_table_destroy (discover_cache); + discover_cache = NULL; + } + } + + return FALSE; +} + +static gboolean +on_idle_complete (gpointer user_data) +{ + RealmKerberosDiscover *self = REALM_KERBEROS_DISCOVER (user_data); + g_assert (self->completed); + kerberos_discover_complete (self); + return FALSE; +} + +void +realm_kerberos_discover_async (const gchar *string, + GDBusMethodInvocation *invocation, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDBusConnection *connection; + RealmKerberosDiscover *self; + Callback *call; + gchar *domain; + Key key; + + g_return_if_fail (string != NULL); + g_return_if_fail (invocation == NULL || G_IS_DBUS_METHOD_INVOCATION (invocation)); + + key.string = (gchar *)string; + key.invocation = invocation; + + if (!discover_cache) { + discover_cache = g_hash_table_new_full (discover_key_hash, discover_key_equal, + NULL, g_object_unref); + } + + self = g_hash_table_lookup (discover_cache, &key); + + if (self == NULL) { + self = g_object_new (REALM_TYPE_KERBEROS_DISCOVER, NULL); + self->key.string = g_strdup (string); + self->key.invocation = g_object_ref (invocation); + + if (g_str_equal (string, "")) { + connection = g_dbus_method_invocation_get_connection (invocation); + realm_diagnostics_info (invocation, "Looking up our DHCP domain"); + realm_network_get_dhcp_domain_async (connection, on_get_dhcp_domain, + g_object_ref (self)); + + } else { + domain = g_ascii_strdown (string, -1); + g_strstrip (domain); + self->domain = domain; + kerberos_discover_domain_begin (self); + } + + g_hash_table_insert (discover_cache, &self->key, self); + g_timeout_add_seconds (5, on_timeout_remove_cache, &self->key); + g_assert (!self->completed); + + } else if (self->completed) { + g_idle_add_full (G_PRIORITY_DEFAULT, on_idle_complete, + g_object_ref (self), g_object_unref); + } + + call = g_slice_new0 (Callback); + call->function = callback; + call->user_data = user_data; + call->next = self->callback; + self->callback = call; +} + +gchar * +realm_kerberos_discover_finish (GAsyncResult *result, + GHashTable **discovery, + GError **error) +{ + RealmKerberosDiscover *self; + gchar *realm; + + g_return_val_if_fail (REALM_IS_KERBEROS_DISCOVER (result), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + self = REALM_KERBEROS_DISCOVER (result); + + /* A failure */ + if (self->error) { + if (error) + *error = g_error_copy (self->error); + return NULL; + } + + /* Didn't find a valid domain */ + if (!self->found_kerberos) + return NULL; + + realm = g_ascii_strup (self->domain, -1); + + if (discovery) { + *discovery = realm_discovery_new (); + + /* The domain */ + realm_discovery_add_string (*discovery, REALM_DBUS_DISCOVERY_DOMAIN, + self->domain); + + /* The realm */ + realm_discovery_add_string (*discovery, REALM_DBUS_DISCOVERY_REALM, realm); + + /* The servers */ + realm_discovery_add_srv_targets (*discovery, REALM_DBUS_DISCOVERY_KDCS, + self->servers); + + /* The type */ + if (self->found_msdcs) { + realm_discovery_add_string (*discovery, + REALM_DBUS_OPTION_SERVER_SOFTWARE, + REALM_DBUS_IDENTIFIER_ACTIVE_DIRECTORY); + + } else if (self->found_ipa) { + realm_discovery_add_string (*discovery, + REALM_DBUS_OPTION_SERVER_SOFTWARE, + REALM_DBUS_IDENTIFIER_FREEIPA); + } + } + + return realm; +} diff --git a/service/realm-kerberos-discover.h b/service/realm-kerberos-discover.h new file mode 100644 index 0000000..913726f --- /dev/null +++ b/service/realm-kerberos-discover.h @@ -0,0 +1,35 @@ +/* realmd -- Realm configuration service + * + * Copyright 2012 Red Hat Inc + * + * This program 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 2 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: Stef Walter + */ + +#include "config.h" + +#ifndef __REALM_KERBEROS_DISCOVER_H__ +#define __REALM_KERBEROS_DISCOVER_H__ + +#include + +G_BEGIN_DECLS + +void realm_kerberos_discover_async (const gchar *string, + GDBusMethodInvocation *invocation, + GAsyncReadyCallback callback, + gpointer user_data); + +gchar * realm_kerberos_discover_finish (GAsyncResult *result, + GHashTable **discovery, + GError **error); + +G_END_DECLS + +#endif /* __REALM_AD_DISCOVER_H__ */ diff --git a/service/realm-samba-provider.c b/service/realm-samba-provider.c index 852ba5d..5fcc0fb 100644 --- a/service/realm-samba-provider.c +++ b/service/realm-samba-provider.c @@ -14,7 +14,6 @@ #include "config.h" -#include "realm-ad-discover.h" #include "realm-command.h" #include "realm-daemon.h" #include "realm-dbus-constants.h" @@ -22,6 +21,7 @@ #include "realm-discovery.h" #include "realm-errors.h" #include "realm-kerberos.h" +#include "realm-kerberos-discover.h" #include "realm-packages.h" #include "realm-samba.h" #include "realm-samba-config.h" @@ -113,8 +113,8 @@ realm_samba_provider_discover_async (RealmProvider *provider, g_simple_async_result_complete_in_idle (async); } else { - realm_ad_discover_async (string, invocation, on_ad_discover, - g_object_ref (async)); + realm_kerberos_discover_async (string, invocation, on_ad_discover, + g_object_ref (async)); } g_object_unref (async); @@ -126,7 +126,7 @@ realm_samba_provider_discover_finish (RealmProvider *provider, GVariant **realms, GError **error) { - RealmKerberos *realm; + RealmKerberos *realm = NULL; GSimpleAsyncResult *async; GHashTable *discovery; GAsyncResult *ad_result; @@ -138,13 +138,19 @@ realm_samba_provider_discover_finish (RealmProvider *provider, if (ad_result == NULL) return 0; - name = realm_ad_discover_finish (ad_result, &discovery, error); + name = realm_kerberos_discover_finish (ad_result, &discovery, error); if (name == NULL) return 0; - realm = realm_provider_lookup_or_register_realm (provider, - REALM_TYPE_SAMBA, - name, discovery); + if (realm_discovery_has_string (discovery, + REALM_DBUS_OPTION_SERVER_SOFTWARE, + REALM_DBUS_IDENTIFIER_ACTIVE_DIRECTORY)) { + + realm = realm_provider_lookup_or_register_realm (provider, + REALM_TYPE_SAMBA, + name, discovery); + } + g_free (name); g_hash_table_unref (discovery); diff --git a/service/realm-samba.c b/service/realm-samba.c index 1e9f3ed..86d88ab 100644 --- a/service/realm-samba.c +++ b/service/realm-samba.c @@ -14,7 +14,6 @@ #include "config.h" -#include "realm-ad-discover.h" #include "realm-command.h" #include "realm-daemon.h" #include "realm-dbus-constants.h" diff --git a/service/realm-sssd-ad-provider.c b/service/realm-sssd-ad-provider.c index ba0789d..b5194bd 100644 --- a/service/realm-sssd-ad-provider.c +++ b/service/realm-sssd-ad-provider.c @@ -14,7 +14,6 @@ #include "config.h" -#include "realm-ad-discover.h" #include "realm-command.h" #include "realm-daemon.h" #include "realm-dbus-constants.h" @@ -22,6 +21,7 @@ #include "realm-discovery.h" #include "realm-errors.h" #include "realm-kerberos.h" +#include "realm-kerberos-discover.h" #include "realm-packages.h" #include "realm-sssd-ad.h" #include "realm-sssd-ad-provider.h" @@ -91,9 +91,9 @@ realm_sssd_ad_provider_constructed (GObject *obj) } static void -on_ad_discover (GObject *source, - GAsyncResult *result, - gpointer user_data) +on_kerberos_discover (GObject *source, + GAsyncResult *result, + gpointer user_data) { GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data); g_simple_async_result_set_op_res_gpointer (async, g_object_ref (result), g_object_unref); @@ -120,8 +120,8 @@ realm_sssd_ad_provider_discover_async (RealmProvider *provider, g_simple_async_result_complete_in_idle (async); } else { - realm_ad_discover_async (string, invocation, on_ad_discover, - g_object_ref (async)); + realm_kerberos_discover_async (string, invocation, on_kerberos_discover, + g_object_ref (async)); } g_object_unref (async); @@ -135,7 +135,7 @@ realm_sssd_ad_provider_discover_finish (RealmProvider *provider, { GSimpleAsyncResult *async; GAsyncResult *ad_result; - RealmKerberos *realm; + RealmKerberos *realm = NULL; GHashTable *discovery; const gchar *object_path; gchar *name; @@ -145,13 +145,19 @@ realm_sssd_ad_provider_discover_finish (RealmProvider *provider, if (ad_result == NULL) return 0; - name = realm_ad_discover_finish (ad_result, &discovery, error); + name = realm_kerberos_discover_finish (ad_result, &discovery, error); if (name == NULL) return 0; - realm = realm_provider_lookup_or_register_realm (provider, - REALM_TYPE_SSSD_AD, - name, discovery); + if (realm_discovery_has_string (discovery, + REALM_DBUS_OPTION_SERVER_SOFTWARE, + REALM_DBUS_IDENTIFIER_ACTIVE_DIRECTORY)) { + + realm = realm_provider_lookup_or_register_realm (provider, + REALM_TYPE_SSSD_AD, + name, discovery); + } + g_free (name); g_hash_table_unref (discovery); diff --git a/service/realm-sssd-ad.c b/service/realm-sssd-ad.c index 7fe49d9..db60a7c 100644 --- a/service/realm-sssd-ad.c +++ b/service/realm-sssd-ad.c @@ -14,7 +14,6 @@ #include "config.h" -#include "realm-ad-discover.h" #include "realm-command.h" #include "realm-dbus-constants.h" #include "realm-diagnostics.h" diff --git a/service/realm-sssd-ipa-provider.c b/service/realm-sssd-ipa-provider.c index 4bf87a5..4b7717d 100644 --- a/service/realm-sssd-ipa-provider.c +++ b/service/realm-sssd-ipa-provider.c @@ -14,7 +14,6 @@ #include "config.h" -#include "realm-ipa-discover.h" #include "realm-command.h" #include "realm-daemon.h" #include "realm-dbus-constants.h" @@ -22,6 +21,7 @@ #include "realm-discovery.h" #include "realm-errors.h" #include "realm-kerberos.h" +#include "realm-kerberos-discover.h" #include "realm-packages.h" #include "realm-sssd-ipa.h" #include "realm-sssd-ipa-provider.h" @@ -120,7 +120,7 @@ realm_sssd_ipa_provider_discover_async (RealmProvider *provider, g_simple_async_result_complete_in_idle (async); } else { - realm_ipa_discover_async (string, invocation, on_ipa_discover, + realm_kerberos_discover_async (string, invocation, on_ipa_discover, g_object_ref (async)); } @@ -136,7 +136,7 @@ realm_sssd_ipa_provider_discover_finish (RealmProvider *provider, { GSimpleAsyncResult *async; GAsyncResult *ipa_result; - RealmKerberos *realm; + RealmKerberos *realm = NULL; GHashTable *discovery; const gchar *object_path; gchar *name; @@ -146,13 +146,19 @@ realm_sssd_ipa_provider_discover_finish (RealmProvider *provider, if (ipa_result == NULL) return 0; - name = realm_ipa_discover_finish (ipa_result, &discovery, error); + name = realm_kerberos_discover_finish (ipa_result, &discovery, error); if (name == NULL) return 0; - realm = realm_provider_lookup_or_register_realm (provider, - REALM_TYPE_SSSD_IPA, - name, discovery); + if (realm_discovery_has_string (discovery, + REALM_DBUS_OPTION_SERVER_SOFTWARE, + REALM_DBUS_IDENTIFIER_FREEIPA)) { + + realm = realm_provider_lookup_or_register_realm (provider, + REALM_TYPE_SSSD_IPA, + name, discovery); + } + g_free (name); g_hash_table_unref (discovery); -- 1.7.12