From 67a1e046ee043362e2d46521754b6e07791fe49d Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Mon, 22 Apr 2013 13:12:58 +0200 Subject: [PATCH] Rework the discovery mechanisms * Actually use MS-CLDAP to connect to AD servers * Use RootDSE lookups to discover IPA servers * Support discovery of a specific server https://bugs.freedesktop.org/show_bug.cgi?id=63907 --- configure.ac | 19 ++ service/Makefile.am | 9 +- service/realm-disco-dns.c | 305 +++++++++++++++++++ service/realm-disco-dns.h | 39 +++ service/realm-disco-domain.c | 365 +++++++++++++++++++++++ service/realm-disco-domain.h | 36 +++ service/realm-disco-mscldap.c | 315 ++++++++++++++++++++ service/realm-disco-mscldap.h | 31 ++ service/realm-disco-rootdse.c | 341 +++++++++++++++++++++ service/realm-disco-rootdse.h | 31 ++ service/realm-disco.c | 66 ++++ service/realm-disco.h | 44 +++ service/realm-discovery.c | 108 ------- service/realm-discovery.h | 50 ---- service/realm-example-provider.c | 4 +- service/realm-ipa-discover.c | 611 -------------------------------------- service/realm-ipa-discover.h | 34 --- service/realm-kerberos-discover.c | 566 ----------------------------------- service/realm-kerberos-discover.h | 35 --- service/realm-kerberos-provider.c | 50 ++-- service/realm-kerberos.c | 58 ++-- service/realm-kerberos.h | 7 +- service/realm-ldap.c | 357 ++++++++++++++++++++++ service/realm-ldap.h | 45 +++ service/realm-options.c | 2 +- service/realm-provider.c | 9 +- service/realm-provider.h | 3 +- service/realm-samba-enroll.c | 234 ++++----------- service/realm-samba-enroll.h | 3 +- service/realm-samba-provider.c | 38 ++- service/realm-samba.c | 5 +- service/realm-sssd-ad.c | 2 +- service/realm-sssd-ipa.c | 2 - service/realm-sssd-provider.c | 45 ++- service/realm-sssd.c | 13 +- 35 files changed, 2177 insertions(+), 1705 deletions(-) create mode 100644 service/realm-disco-dns.c create mode 100644 service/realm-disco-dns.h create mode 100644 service/realm-disco-domain.c create mode 100644 service/realm-disco-domain.h create mode 100644 service/realm-disco-mscldap.c create mode 100644 service/realm-disco-mscldap.h create mode 100644 service/realm-disco-rootdse.c create mode 100644 service/realm-disco-rootdse.h create mode 100644 service/realm-disco.c create mode 100644 service/realm-disco.h delete mode 100644 service/realm-discovery.c delete mode 100644 service/realm-discovery.h delete mode 100644 service/realm-ipa-discover.c delete mode 100644 service/realm-ipa-discover.h delete mode 100644 service/realm-kerberos-discover.c delete mode 100644 service/realm-kerberos-discover.h create mode 100644 service/realm-ldap.c create mode 100644 service/realm-ldap.h diff --git a/configure.ac b/configure.ac index 3db8d43..891eb19 100644 --- a/configure.ac +++ b/configure.ac @@ -122,6 +122,25 @@ AC_SUBST(SYSTEMD_JOURNAL_CFLAGS) AC_SUBST(SYSTEMD_JOURNAL_LIBS) # ------------------------------------------------------------------- +# resolv + +AC_MSG_CHECKING(for which library has res_query) +for lib in "" "-lresolv"; do + saved_LIBS="$LIBS" + LIBS="$LIBS $lib" + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([#include ], + [res_query (0, 0, 0, 0, 0)]) + ], + [ AC_MSG_RESULT(${lib:-libc}); have_res_query="yes"; break; ], + [ LIBS="$saved_LIBS" ]) +done +if test "$have_res_query" != "yes"; then + AC_MSG_RESULT(no) + AC_MSG_ERROR([Couldn't find the library for the res_query function]) +fi + +# ------------------------------------------------------------------- # Kerberos if test "$KRB5_LIBS" != ""; then diff --git a/service/Makefile.am b/service/Makefile.am index 7662c5f..896a030 100644 --- a/service/Makefile.am +++ b/service/Makefile.am @@ -26,17 +26,20 @@ realmd_SOURCES = \ realm-credential.c realm-credential.h \ realm-daemon.c realm-daemon.h \ realm-diagnostics.c realm-diagnostics.h \ - realm-discovery.c realm-discovery.h \ + realm-disco.c realm-disco.h \ + realm-disco-dns.c realm-disco-dns.h \ + realm-disco-domain.c realm-disco-domain.h \ + realm-disco-mscldap.c realm-disco-mscldap.h \ + realm-disco-rootdse.c realm-disco-rootdse.h \ realm-errors.c realm-errors.h \ realm-example.c realm-example.h \ realm-example-provider.c realm-example-provider.h \ realm-ini-config.c realm-ini-config.h \ realm-invocation.c realm-invocation.h \ - realm-ipa-discover.c realm-ipa-discover.h \ realm-kerberos.c realm-kerberos.h \ - realm-kerberos-discover.c realm-kerberos-discover.h \ realm-kerberos-membership.c realm-kerberos-membership.h \ realm-kerberos-provider.c realm-kerberos-provider.h \ + realm-ldap.c realm-ldap.h \ realm-login-name.c realm-login-name.h \ realm-network.c realm-network.h \ realm-options.c realm-options.h \ diff --git a/service/realm-disco-dns.c b/service/realm-disco-dns.c new file mode 100644 index 0000000..803aaf5 --- /dev/null +++ b/service/realm-disco-dns.c @@ -0,0 +1,305 @@ +/* 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 "egg-task.h" +#include "realm-diagnostics.h" +#include "realm-disco-dns.h" + +#include + +typedef enum { + PHASE_NONE, + PHASE_MSDCS, + PHASE_SRV, + PHASE_HOST, + PHASE_DONE +} DiscoPhase; + +typedef struct { + GSocketAddressEnumerator parent; + gchar *name; + GQueue addresses; + GQueue targets; + gint current_port; + gint returned; + DiscoPhase phase; + GResolver *resolver; + GDBusMethodInvocation *invocation; +} RealmDiscoDns; + +typedef struct { + GSocketAddressEnumeratorClass parent; +} RealmDiscoDnsClass; + +#define REALM_TYPE_DISCO_DNS (realm_disco_dns_get_type ()) +#define REALM_DISCO_DNS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), REALM_TYPE_DISCO_DNS, RealmDiscoDns)) +#define REALM_IS_DISCO_DNS(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), REALM_TYPE_DISCO_DNS)) + +static void return_or_resolve (RealmDiscoDns *self, EggTask *task); + +GType realm_disco_dns_get_type (void) G_GNUC_CONST; + +G_DEFINE_TYPE (RealmDiscoDns, realm_disco_dns, G_TYPE_SOCKET_ADDRESS_ENUMERATOR); + +static void +realm_disco_dns_init (RealmDiscoDns *self) +{ + g_queue_init (&self->addresses); + g_queue_init (&self->targets); +} + +static void +realm_disco_dns_finalize (GObject *obj) +{ + RealmDiscoDns *self = REALM_DISCO_DNS (obj); + gpointer value; + + g_free (self->name); + g_object_unref (self->invocation); + g_clear_object (&self->resolver); + + for (;;) { + value = g_queue_pop_head (&self->addresses); + if (!value) + break; + g_object_unref (value); + } + + for (;;) { + value = g_queue_pop_head (&self->targets); + if (!value) + break; + g_srv_target_free (value); + } + + G_OBJECT_CLASS (realm_disco_dns_parent_class)->finalize (obj); +} + +static GSocketAddress * +realm_disco_dns_next (GSocketAddressEnumerator *enumerator, + GCancellable *cancellable, + GError **error) +{ + /* We don't use this synchronously in realmd */ + g_return_val_if_reached (NULL); +} + +static void +on_name_resolved (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + EggTask *task = EGG_TASK (user_data); + RealmDiscoDns *self = egg_task_get_source_object (task); + GError *error = NULL; + GList *addrs; + GList *l; + + addrs = g_resolver_lookup_by_name_finish (self->resolver, result, &error); + + /* These are not real errors, just absence of addresses */ + if (g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND) || + g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE)) + g_clear_error (&error); + + if (error) { + egg_task_return_error (task, error); + + } else { + for (l = addrs; l != NULL; l = g_list_next (l)) + g_queue_push_head (&self->addresses, g_inet_socket_address_new (l->data, self->current_port)); + g_list_free_full (addrs, g_object_unref); + return_or_resolve (self, task); + } + + g_object_unref (task); +} + + +static void +on_service_resolved (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + EggTask *task = EGG_TASK (user_data); + RealmDiscoDns *self = egg_task_get_source_object (task); + GError *error = NULL; + GList *targets; + GList *l; + + targets = g_resolver_lookup_service_finish (self->resolver, result, &error); + + /* These are not real errors, just absence of addresses */ + if (g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND) || + g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE)) + g_clear_error (&error); + + if (error) { + egg_task_return_error (task, error); + + } else { + for (l = targets; l != NULL; l = g_list_next (l)) + g_queue_push_tail (&self->targets, l->data); + g_list_free (targets); + return_or_resolve (self, task); + } + + g_object_unref (task); +} + +static void +return_or_resolve (RealmDiscoDns *self, + EggTask *task) +{ + GSocketAddress *address; + GSrvTarget *target; + gchar *msdcs; + + address = g_queue_pop_head (&self->addresses); + if (address) { + self->returned++; + egg_task_return_pointer (task, address, g_object_unref); + return; + } + + target = g_queue_pop_head (&self->targets); + if (target) { + g_resolver_lookup_by_name_async (self->resolver, g_srv_target_get_hostname (target), + egg_task_get_cancellable (task), on_name_resolved, + g_object_ref (task)); + g_srv_target_free (target); + return; + } + + switch (self->returned > 0 ? PHASE_DONE : self->phase) { + case PHASE_NONE: + msdcs = g_strdup_printf ("dc._msdcs.%s", self->name); + realm_diagnostics_info (self->invocation, "Resolving: _ldap._tcp.%s", msdcs); + g_resolver_lookup_service_async (self->resolver, "ldap", "tcp", msdcs, + egg_task_get_cancellable (task), + on_service_resolved, g_object_ref (task)); + self->phase = PHASE_MSDCS; + g_free (msdcs); + break; + case PHASE_MSDCS: + realm_diagnostics_info (self->invocation, "Resolving: _ldap._tcp.%s", self->name); + g_resolver_lookup_service_async (self->resolver, "ldap", "tcp", self->name, + egg_task_get_cancellable (task), + on_service_resolved, g_object_ref (task)); + self->phase = PHASE_SRV; + break; + case PHASE_SRV: + realm_diagnostics_info (self->invocation, "Resolving: %s", self->name); + g_resolver_lookup_by_name_async (self->resolver, self->name, + egg_task_get_cancellable (task), on_name_resolved, + g_object_ref (task)); + self->current_port = 389; + self->phase = PHASE_HOST; + break; + case PHASE_HOST: + case PHASE_DONE: + egg_task_return_pointer (task, NULL, NULL); + self->phase = PHASE_DONE; + break; + } +} + +static void +realm_disco_dns_next_async (GSocketAddressEnumerator *enumerator, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + RealmDiscoDns *self = REALM_DISCO_DNS (enumerator); + EggTask *task; + + task = egg_task_new (enumerator, cancellable, callback, user_data); + return_or_resolve (self, task); + g_object_unref (task); +} + +static GSocketAddress * +realm_disco_dns_next_finish (GSocketAddressEnumerator *enumerator, + GAsyncResult *result, + GError **error) +{ + return egg_task_propagate_pointer (EGG_TASK (result), error); +} + +static void +realm_disco_dns_class_init (RealmDiscoDnsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GSocketAddressEnumeratorClass *enum_class = G_SOCKET_ADDRESS_ENUMERATOR_CLASS (klass); + + object_class->finalize = realm_disco_dns_finalize; + + enum_class->next = realm_disco_dns_next; + enum_class->next_async = realm_disco_dns_next_async; + enum_class->next_finish = realm_disco_dns_next_finish; +} + +GSocketAddressEnumerator * +realm_disco_dns_enumerate_servers (const gchar *domain_or_server, + GDBusMethodInvocation *invocation) +{ + RealmDiscoDns *self; + GInetAddress *inet; + gchar *input; + + input = g_strdup (domain_or_server); + g_strstrip (input); + + self = g_object_new (REALM_TYPE_DISCO_DNS, NULL); + self->name = g_hostname_to_ascii (input); + self->invocation = g_object_ref (invocation); + + /* If is an IP, skip resolution */ + if (g_hostname_is_ip_address (input)) { + inet = g_inet_address_new_from_string (input); + g_queue_push_head (&self->addresses, g_inet_socket_address_new (inet, 389)); + g_object_unref (inet); + self->phase = PHASE_HOST; + } else { + self->resolver = g_resolver_get_default (); + } + + g_free (input); + return G_SOCKET_ADDRESS_ENUMERATOR (self); +} + +RealmDiscoDnsHint +realm_disco_dns_get_hint (GSocketAddressEnumerator *enumerator) +{ + g_return_val_if_fail (REALM_IS_DISCO_DNS (enumerator), FALSE); + switch (REALM_DISCO_DNS (enumerator)->phase) { + case PHASE_MSDCS: + return REALM_DISCO_HINT_IS_MSDCS; + case PHASE_SRV: + return REALM_DISCO_HINT_IS_NOT_MSDCS; + case PHASE_HOST: + return REALM_DISCO_IS_SERVER; + default: + return 0; + } +} + +const gchar * +realm_disco_dns_get_name (GSocketAddressEnumerator *enumerator) +{ + g_return_val_if_fail (REALM_IS_DISCO_DNS (enumerator), NULL); + return REALM_DISCO_DNS (enumerator)->name; +} diff --git a/service/realm-disco-dns.h b/service/realm-disco-dns.h new file mode 100644 index 0000000..cb04d57 --- /dev/null +++ b/service/realm-disco-dns.h @@ -0,0 +1,39 @@ +/* realmd -- Realm configuration service + * + * Copyright 2013 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_DISCO_DNS_H__ +#define __REALM_DICSO_DNS_H__ + +#include + +typedef enum { + REALM_DISCO_HINT_IS_MSDCS = 1 << 1, + REALM_DISCO_HINT_IS_NOT_MSDCS = 1 << 2, + REALM_DISCO_IS_SERVER = 1 << 3, +} RealmDiscoDnsHint; + +G_BEGIN_DECLS + +GSocketAddressEnumerator * realm_disco_dns_enumerate_servers (const gchar *domain_or_server, + GDBusMethodInvocation *invocation); + +RealmDiscoDnsHint realm_disco_dns_get_hint (GSocketAddressEnumerator *enumerator); + +const gchar * realm_disco_dns_get_name (GSocketAddressEnumerator *enumerator); + +G_END_DECLS + +#endif /* __REALM_DISCO_DNS_H__ */ diff --git a/service/realm-disco-domain.c b/service/realm-disco-domain.c new file mode 100644 index 0000000..3cf7e54 --- /dev/null +++ b/service/realm-disco-domain.c @@ -0,0 +1,365 @@ +/* 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-disco.h" +#include "realm-disco-dns.h" +#include "realm-disco-domain.h" +#include "realm-disco-mscldap.h" +#include "realm-disco-rootdse.h" +#include "realm-errors.h" +#include "realm-invocation.h" +#include "realm-network.h" + +#include + +#define TIMEOUT_SECONDS 15 + +typedef struct _Callback { + GAsyncReadyCallback function; + gpointer user_data; + struct _Callback *next; +} Callback; + +typedef struct { + GObject parent; + gchar *input; + GCancellable *cancellable; + GDBusMethodInvocation *invocation; + GSocketAddressEnumerator *enumerator; + gint outstanding; + gboolean completed; + RealmDisco *disco; + Callback *callback; + guint timeout_id; +} RealmDiscoDomain; + +typedef struct { + GObjectClass parent; +} RealmDiscoDomainClass; + +#define REALM_TYPE_DISCO_DOMAIN (realm_disco_domain_get_type ()) +#define REALM_DISCO_DOMAIN(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), REALM_TYPE_DISCO_DOMAIN, RealmDiscoDomain)) +#define REALM_IS_DISCO_DOMAIN(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), REALM_TYPE_DISCO_DOMAIN)) + +static GHashTable *discover_cache = NULL; + +static void step_discover (RealmDiscoDomain *self, + RealmDisco *disco); + +GType realm_disco_domain_get_type (void) G_GNUC_CONST; + +void realm_disco_domain_async_result_init (GAsyncResultIface *iface); + +G_DEFINE_TYPE_WITH_CODE (RealmDiscoDomain, realm_disco_domain, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, realm_disco_domain_async_result_init); +); + +static void +realm_disco_domain_init (RealmDiscoDomain *self) +{ + self->cancellable = g_cancellable_new (); +} + +static void +realm_disco_domain_finalize (GObject *obj) +{ + RealmDiscoDomain *self = REALM_DISCO_DOMAIN (obj); + + g_free (self->input); + g_object_unref (self->cancellable); + g_object_unref (self->invocation); + g_clear_object (&self->enumerator); + realm_disco_unref (self->disco); + + g_assert (self->callback == NULL); + G_OBJECT_CLASS (realm_disco_domain_parent_class)->finalize (obj); +} + +static void +realm_disco_domain_class_init (RealmDiscoDomainClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = realm_disco_domain_finalize; +} + +static GObject * +realm_disco_domain_get_source_object (GAsyncResult *result) +{ + return g_object_ref (result); +} + +static gpointer +realm_disco_domain_get_user_data (GAsyncResult *result) +{ + /* What is this useful for? */ + g_return_val_if_reached (NULL); +} + +void +realm_disco_domain_async_result_init (GAsyncResultIface *iface) +{ + iface->get_source_object = realm_disco_domain_get_source_object; + iface->get_user_data = realm_disco_domain_get_user_data; +} + +typedef struct { + Callback *clinger; +} DiscoverClosure; + +static void +complete_discover (RealmDiscoDomain *self) +{ + Callback *call, *next; + + g_assert (!self->completed); + self->completed = TRUE; + + if (self->timeout_id) + g_source_remove (self->timeout_id); + self->timeout_id = 0; + + /* No longer in the concurrency cache */ + g_hash_table_remove (discover_cache, self->input); + if (g_hash_table_size (discover_cache) == 0) + g_hash_table_destroy (discover_cache); + discover_cache = NULL; + + /* Stop all other results */ + g_cancellable_cancel (self->cancellable); + + call = self->callback; + self->callback = NULL; + + if (self->disco) + realm_diagnostics_info (self->invocation, "Successfully discovered: %s", self->disco->domain_name); + + 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; + } +} + +static void +on_discover_mscldap (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + RealmDiscoDomain *self = REALM_DISCO_DOMAIN (user_data); + GError *error = NULL; + RealmDisco *disco; + + self->outstanding--; + disco = realm_disco_mscldap_finish (result, &error); + + if (error && !self->completed) + realm_diagnostics_error (self->invocation, error, NULL); + g_clear_error (&error); + step_discover (self, disco); + + g_object_unref (self); +} + +static void +on_discover_rootdse (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + RealmDiscoDomain *self = REALM_DISCO_DOMAIN (user_data); + GError *error = NULL; + RealmDisco *disco; + + self->outstanding--; + disco = realm_disco_rootdse_finish (result, &error); + + if (error && !self->completed) + realm_diagnostics_error (self->invocation, error, NULL); + g_clear_error (&error); + step_discover (self, disco); + + g_object_unref (self); +} + +static void +on_discover_next_address (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + RealmDiscoDomain *self = REALM_DISCO_DOMAIN (user_data); + GError *error = NULL; + GSocketAddress *address; + GInetSocketAddress *inet; + const gchar *explicit_host; + RealmDiscoDnsHint hint; + gchar *string; + + if (self->completed) { + g_object_unref (self); + return; + } + + address = g_socket_address_enumerator_next_finish (G_SOCKET_ADDRESS_ENUMERATOR (source), result, &error); + if (error != NULL || address == NULL) { + if (error && !self->completed) + realm_diagnostics_error (self->invocation, error, "Couldn't lookup realm DNS records"); + g_clear_error (&error); + g_clear_object (&self->enumerator); + + } else { + inet = G_INET_SOCKET_ADDRESS (address); + string = g_inet_address_to_string (g_inet_socket_address_get_address (inet)); + + hint = realm_disco_dns_get_hint (self->enumerator); + if (hint & REALM_DISCO_IS_SERVER) + explicit_host = realm_disco_dns_get_name (self->enumerator); + else + explicit_host = NULL; + if (!(hint & REALM_DISCO_HINT_IS_NOT_MSDCS)) { + realm_diagnostics_info (self->invocation, "Sending MS-CLDAP ping to: %s", string); + realm_disco_mscldap_async (address, explicit_host, + self->cancellable, on_discover_mscldap, + g_object_ref (self)); + self->outstanding++; + } + if (!(hint & REALM_DISCO_HINT_IS_MSDCS)) { + realm_diagnostics_info (self->invocation, "Performing LDAP DSE lookup on: %s", string); + realm_disco_rootdse_async (address, explicit_host, + self->cancellable, on_discover_rootdse, + g_object_ref (self)); + self->outstanding++; + } + + g_free (string); + } + + step_discover (self, NULL); + g_clear_object (&address); + g_object_unref (self); +} + +static void +step_discover (RealmDiscoDomain *self, + RealmDisco *disco) +{ + /* Already done, just skip these results */ + if (self->completed) { + realm_disco_unref (disco); + + /* Either have a result, or finished searching: done */ + } else if (disco || (self->enumerator == NULL && self->outstanding == 0)) { + self->disco = disco; + complete_discover (self); + + /* Otherwise try up to three servers at once */ + } else if (self->enumerator && self->outstanding < 3) { + g_socket_address_enumerator_next_async (self->enumerator, + self->cancellable, + on_discover_next_address, + g_object_ref (self)); + } +} + +static void +on_cancel_propagate (GCancellable *source, + gpointer dest) +{ + g_cancellable_cancel (dest); +} + +static gboolean +on_discover_timeout (gpointer user_data) +{ + RealmDiscoDomain *self = REALM_DISCO_DOMAIN (user_data); + realm_diagnostics_error (self->invocation, NULL, + "Discovery timed out after %d seconds", + TIMEOUT_SECONDS); + self->timeout_id = 0; + complete_discover (self); + return FALSE; +} + +void +realm_disco_domain_async (const gchar *string, + GDBusMethodInvocation *invocation, + GAsyncReadyCallback callback, + gpointer user_data) +{ + RealmDiscoDomain *self; + GCancellable *cancellable; + Callback *call; + + g_return_if_fail (string != NULL); + g_return_if_fail (invocation == NULL || G_IS_DBUS_METHOD_INVOCATION (invocation)); + + if (!discover_cache) + discover_cache = g_hash_table_new (g_str_hash, g_str_equal); + + self = g_hash_table_lookup (discover_cache, string); + + if (self == NULL) { + self = g_object_new (REALM_TYPE_DISCO_DOMAIN, NULL); + self->input = g_strdup (string); + self->invocation = g_object_ref (invocation); + self->enumerator = realm_disco_dns_enumerate_servers (string, invocation); + + g_hash_table_insert (discover_cache, self->input, self); + g_assert (!self->completed); + + cancellable = realm_invocation_get_cancellable (invocation); + if (cancellable) { + g_cancellable_connect (cancellable, (GCallback)on_cancel_propagate, + g_object_ref (self->cancellable), g_object_unref); + } + + self->timeout_id = g_timeout_add_seconds (TIMEOUT_SECONDS, on_discover_timeout, self); + step_discover (self, NULL); + + } else { + g_assert (!self->completed); + g_object_ref (self); + } + + call = g_slice_new0 (Callback); + call->function = callback; + call->user_data = user_data; + call->next = self->callback; + self->callback = call; + + g_object_unref (self); +} + +RealmDisco * +realm_disco_domain_finish (GAsyncResult *result, + GError **error) +{ + RealmDiscoDomain *self; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + self = REALM_DISCO_DOMAIN (result); + + /* Didn't find a valid domain */ + if (!self->disco) + return NULL; + + return realm_disco_ref (self->disco); +} diff --git a/service/realm-disco-domain.h b/service/realm-disco-domain.h new file mode 100644 index 0000000..27dcc6c --- /dev/null +++ b/service/realm-disco-domain.h @@ -0,0 +1,36 @@ +/* realmd -- Realm configuration service + * + * Copyright 2013 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_DISCO_DOMAIN_H__ +#define __REALM_DICSO_DOMAIN_H__ + +#include "realm-disco.h" + +#include + +G_BEGIN_DECLS + +void realm_disco_domain_async (const gchar *string, + GDBusMethodInvocation *invocation, + GAsyncReadyCallback callback, + gpointer user_data); + +RealmDisco * realm_disco_domain_finish (GAsyncResult *result, + GError **error); + +G_END_DECLS + +#endif /* __REALM_DISCO_DOMAIN_H__ */ diff --git a/service/realm-disco-mscldap.c b/service/realm-disco-mscldap.c new file mode 100644 index 0000000..d9378be --- /dev/null +++ b/service/realm-disco-mscldap.c @@ -0,0 +1,315 @@ +/* realmd -- Realm configuration service + * + * Copyright 2013 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 "egg-task.h" +#include "realm-dbus-constants.h" +#include "realm-disco-mscldap.h" +#include "realm-ldap.h" + +#include + +#include + +typedef struct { + gchar *explicit_server; + GSocketProtocol protocol; + GSource *source; + gint sock; + gint count; + gint fever_id; + gint normal_id; +} Closure; + +/* Number of rapid requets to do */ +#define DISCO_FEVER 4 + +static void +closure_free (gpointer data) +{ + Closure *clo = data; + + g_free (clo->explicit_server); + if (clo->fever_id) + g_source_remove (clo->fever_id); + if (clo->normal_id) + g_source_remove (clo->normal_id); + g_source_destroy (clo->source); + g_source_unref (clo->source); + g_slice_free (Closure, clo); +} + +static gchar * +get_string (guchar *beg, + guchar *end, + guchar **at) +{ + gchar buffer[HOST_NAME_MAX]; + int n; + + n = dn_expand (beg, end, *at, buffer, sizeof (buffer)); + if (n < 0) + return NULL; + + (*at) += n; + return g_strdup (buffer); +} + +static gboolean +parse_string (guchar *beg, + guchar *end, + guchar **at, + gchar **result) +{ + gchar *string; + + g_assert (result); + + string = get_string (beg, end, at); + if (string == NULL) + return 0; + + g_free (*result); + *result = string; + + return 1; +} + +static int +get_32_le (unsigned char **at, + unsigned char *end, + unsigned int *val) +{ + unsigned char *p = *at; + if (p + 4 > end) + return 0; + *val = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; + (*at) += 4; + return 1; +} + +static int +skip_n (unsigned char **at, + unsigned char *end, + int n) +{ + if ((*at) + n > end) + return 0; + (*at) += n; + return 1; +} + +static RealmDisco * +parse_netlogon (struct berval **bvs, + GError **error) +{ + RealmDisco *disco = NULL; + guchar *at, *end, *beg; + gchar *unused = NULL; + guint type, flags; + + if (bvs != NULL && bvs[0] != NULL) { + beg = (guchar *)bvs[0]->bv_val; + end = beg + bvs[0]->bv_len; + at = beg; + disco = realm_disco_new (NULL); + } + + /* domain forest */ + if (disco == NULL || + !get_32_le (&at, end, &type) || type != 23 || + !get_32_le (&at, end, &flags) || + !skip_n (&at, end, 16) || /* guid */ + !parse_string (beg, end, &at, &unused) || /* forest */ + !parse_string (beg, end, &at, &disco->domain_name) || + !parse_string (beg, end, &at, &unused) || /* hostname */ + !parse_string (beg, end, &at, &disco->workgroup) || + !parse_string (beg, end, &at, &unused) || /* shorthost */ + !parse_string (beg, end, &at, &unused) || /* user */ + !parse_string (beg, end, &at, &unused) || /* server site */ + !parse_string (beg, end, &at, &unused)) { /* client site */ + realm_disco_unref (disco); + disco = NULL; + } + + g_free (unused); + + if (disco == NULL) { + g_set_error (error, REALM_LDAP_ERROR, LDAP_PROTOCOL_ERROR, + _("Received invalid Netlogon data from server")); + return NULL; + } + + disco->server_software = REALM_DBUS_IDENTIFIER_ACTIVE_DIRECTORY; + disco->kerberos_realm = g_ascii_strup (disco->domain_name, -1); + return disco; +} + +static RealmDisco * +parse_disco (LDAP *ldap, + LDAPMessage *message, + GError **error) +{ + RealmDisco *disco = NULL; + struct berval **bvs = NULL; + LDAPMessage *entry; + + entry = ldap_first_entry (ldap, message); + if (entry != NULL) + bvs = ldap_get_values_len (ldap, entry, "NetLogon"); + disco = parse_netlogon (bvs, error); + ldap_value_free_len (bvs); + + return disco; +} + +static gboolean +on_resend (gpointer user_data) +{ + realm_ldap_set_condition (user_data, G_IO_OUT | G_IO_IN); + return TRUE; +} + +static GIOCondition +on_ldap_io (LDAP *ldap, + GIOCondition cond, + gpointer user_data) +{ + EggTask *task = EGG_TASK (user_data); + Closure *clo = egg_task_get_task_data (task); + char *attrs[] = { "NetLogon", NULL }; + struct timeval tvpoll = { 0, 0 }; + LDAPMessage *message; + GError *error = NULL; + RealmDisco *disco; + int msgid; + gint rc; + + /* Cancelled */ + if (cond & G_IO_ERR) { + realm_ldap_set_error (&error, ldap, 0); + egg_task_return_error (task, error); + return G_IO_NVAL; + } + + /* Ready for input */ + if (cond & G_IO_OUT) { + g_debug ("Sending Netlogon ping"); + rc = ldap_search_ext (ldap, "", LDAP_SCOPE_BASE, + "(&(NtVer=\\06\\00\\00\\00)(AAC=\\00\\00\\00\\00))", + attrs, 0, NULL, NULL, NULL, + -1, &msgid); + + if (rc != LDAP_SUCCESS) { + realm_ldap_set_error (&error, ldap, rc); + egg_task_return_error (task, error); + return G_IO_NVAL; + } + + /* Remove rapid fire after sending a feverish batch */ + if (clo->count++ > DISCO_FEVER && clo->fever_id != 0) { + g_source_remove (clo->fever_id); + clo->fever_id = 0; + } + } + + /* Ready to get a result */ + if (cond & G_IO_IN) { + switch (ldap_result (ldap, LDAP_RES_ANY, 0, &tvpoll, &message)) { + case LDAP_RES_SEARCH_ENTRY: + g_debug ("Received response"); + disco = parse_disco (ldap, message, &error); + if (disco && clo->explicit_server) + disco->explicit_server = g_strdup (clo->explicit_server); + egg_task_return_pointer (task, disco, realm_disco_unref); + ldap_msgfree (message); + return G_IO_NVAL; + case LDAP_RES_SEARCH_RESULT: + egg_task_return_pointer (task, NULL, NULL); + ldap_msgfree (message); + return G_IO_NVAL; + case -1: + realm_ldap_set_error (&error, ldap, -1); + return G_IO_NVAL; + case 0: + break; + default: + /* Ignore and keep waiting */ + ldap_msgfree (message); + break; + } + } + + /* Now wait for a response */ + return G_IO_IN; +} + +static GSocketProtocol +get_cldap_protocol (void) +{ + static gboolean checked = FALSE; + static GSocketProtocol protocol; + + if (!checked) { + if (ldap_is_ldap_url ("cldap://hostname")) + protocol = G_SOCKET_PROTOCOL_UDP; + else + protocol = G_SOCKET_PROTOCOL_TCP; + checked = TRUE; + } + + return protocol; +} + +void +realm_disco_mscldap_async (GSocketAddress *address, + const gchar *explicit_server, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + EggTask *task; + Closure *clo; + + g_return_if_fail (address != NULL); + + task = egg_task_new (NULL, cancellable, callback, user_data); + clo = g_slice_new0 (Closure); + clo->explicit_server = g_strdup (explicit_server); + clo->protocol = get_cldap_protocol (); + egg_task_set_task_data (task, clo, closure_free); + + clo->source = realm_ldap_connect_anonymous (address, clo->protocol, + cancellable); + g_source_set_callback (clo->source, (GSourceFunc)on_ldap_io, + g_object_ref (task), g_object_unref); + g_source_attach (clo->source, egg_task_get_context (task)); + + if (clo->protocol == G_SOCKET_PROTOCOL_UDP) { + clo->fever_id = g_timeout_add (100, on_resend, clo->source); + clo->normal_id = g_timeout_add (1000, on_resend, clo->source); + } + + g_object_unref (task); +} + +RealmDisco * +realm_disco_mscldap_finish (GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (egg_task_is_valid (result, NULL), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + return egg_task_propagate_pointer (EGG_TASK (result), error); +} diff --git a/service/realm-disco-mscldap.h b/service/realm-disco-mscldap.h new file mode 100644 index 0000000..9bf9d65 --- /dev/null +++ b/service/realm-disco-mscldap.h @@ -0,0 +1,31 @@ +/* realmd -- Realm configuration service + * + * Copyright 2013 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 + */ + +#ifndef __REALM_DISCO_MSCLDAP_H__ +#define __REALM_DISCO_MSCLDAP_H__ + +#include + +#include "realm-disco.h" + +void realm_disco_mscldap_async (GSocketAddress *address, + const gchar *explicit_server, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +RealmDisco * realm_disco_mscldap_finish (GAsyncResult *result, + GError **error); + +#endif /* __REALM_DISCO_MSCLDAP_H__ */ diff --git a/service/realm-disco-rootdse.c b/service/realm-disco-rootdse.c new file mode 100644 index 0000000..cc01bee --- /dev/null +++ b/service/realm-disco-rootdse.c @@ -0,0 +1,341 @@ +/* realmd -- Realm configuration service + * + * Copyright 2013 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 "egg-task.h" +#include "realm-dbus-constants.h" +#include "realm-disco-rootdse.h" +#include "realm-ldap.h" + +#include + +#include + +typedef struct _Closure Closure; + +struct _Closure { + RealmDisco *disco; + GSource *source; + + gchar *default_naming_context; + gint msgid; + + gboolean (* request) (EggTask *task, + Closure *clo, + LDAP *ldap); + + gboolean (* result) (EggTask *task, + Closure *clo, + LDAP *ldap, + LDAPMessage *msg); +}; + +static void +closure_free (gpointer data) +{ + Closure *clo = data; + + g_free (clo->default_naming_context); + + g_source_destroy (clo->source); + g_source_unref (clo->source); + realm_disco_unref (clo->disco); + g_slice_free (Closure, clo); +} + +static gboolean +search_ldap (EggTask *task, + Closure *clo, + LDAP *ldap, + const gchar *base, + int scope, + const char *filter, + const char **attrs) +{ + GError *error = NULL; + int rc; + + if (!filter) + filter = "(objectClass=*)"; + + g_debug ("Searching %s for %s", base, filter); + rc = ldap_search_ext (ldap, base, scope, filter, + (char **)attrs, 0, NULL, NULL, NULL, -1, &clo->msgid); + + if (rc != 0) { + realm_ldap_set_error (&error, ldap, rc); + egg_task_return_error (task, error); + return FALSE; + } + + return TRUE; +} + +static gboolean +result_krb_realm (EggTask *task, + Closure *clo, + LDAP *ldap, + LDAPMessage *message) +{ + LDAPMessage *entry; + struct berval **bvs; + + entry = ldap_first_entry (ldap, message); + + g_free (clo->disco->kerberos_realm); + clo->disco->kerberos_realm = NULL; + + if (entry != NULL) + bvs = ldap_get_values_len (ldap, entry, "cn"); + if (bvs && bvs[0]) + clo->disco->kerberos_realm = g_strndup (bvs[0]->bv_val, bvs[0]->bv_len); + ldap_value_free_len (bvs); + + g_debug ("Found realm: %s", clo->disco->kerberos_realm); + + /* All done */ + egg_task_return_boolean (task, TRUE); + return FALSE; +} + +static gboolean +request_krb_realm (EggTask *task, + Closure *clo, + LDAP *ldap) +{ + const char *attrs[] = { "cn", NULL }; + + clo->request = NULL; + clo->result = result_krb_realm; + + return search_ldap (task, clo, ldap, clo->default_naming_context, + LDAP_SCOPE_SUB, "(objectClass=krbRealmContainer)", attrs); +} + +static gboolean +result_domain_info (EggTask *task, + Closure *clo, + LDAP *ldap, + LDAPMessage *message) +{ + LDAPMessage *entry; + struct berval **bvs; + + entry = ldap_first_entry (ldap, message); + + /* If we can't retrieve this, then nothing more to do */ + if (entry == NULL) { + g_debug ("Couldn't read default naming context"); + egg_task_return_new_error (task, REALM_LDAP_ERROR, LDAP_NO_SUCH_OBJECT, + "Couldn't lookup domain name on LDAP server"); + return FALSE; + } + + /* What kind of server is it? */ + clo->disco->server_software = NULL; + bvs = ldap_get_values_len (ldap, entry, "info"); + if (bvs && bvs[0] && bvs[0]->bv_len >= 3) { + if (g_ascii_strncasecmp (bvs[0]->bv_val, "IPA", 3) == 0) + clo->disco->server_software = REALM_DBUS_IDENTIFIER_FREEIPA; + } + ldap_value_free_len (bvs); + + /* What is the domain name? */ + g_free (clo->disco->domain_name); + clo->disco->domain_name = NULL; + bvs = ldap_get_values_len (ldap, entry, "associatedDomain"); + if (bvs && bvs[0]) + clo->disco->domain_name = g_strndup (bvs[0]->bv_val, bvs[0]->bv_len); + ldap_value_free_len (bvs); + + g_debug ("Got associatedDomain: %s", clo->disco->domain_name); + + /* Next search for Kerberos container */ + clo->request = request_krb_realm; + clo->result = NULL; + return TRUE; +} + +static gboolean +request_domain_info (EggTask *task, + Closure *clo, + LDAP *ldap) +{ + const char *attrs[] = { "info", "associatedDomain", NULL }; + + clo->request = NULL; + clo->result = result_domain_info; + + return search_ldap (task, clo, ldap, clo->default_naming_context, + LDAP_SCOPE_BASE, NULL, attrs); +} + +static gboolean +result_root_dse (EggTask *task, + Closure *clo, + LDAP *ldap, + LDAPMessage *message) +{ + LDAPMessage *entry; + struct berval **bvs; + LDAPDN dn; + + entry = ldap_first_entry (ldap, message); + + /* Parse out the default naming context */ + if (entry != NULL) + bvs = ldap_get_values_len (ldap, entry, "defaultNamingContext"); + if (!bvs || !bvs[0] || ldap_bv2dn (bvs[0], &dn, 0) != 0) + dn = NULL; + ldap_value_free_len (bvs); + + if (dn == NULL) { + egg_task_return_new_error (task, REALM_LDAP_ERROR, LDAP_NO_SUCH_OBJECT, + "Couldn't find default naming context on LDAP server"); + return FALSE; + } + + ldap_memfree (clo->default_naming_context); + if (ldap_dn2str (dn, &clo->default_naming_context, LDAP_DN_FORMAT_LDAPV3) != 0) + g_return_val_if_reached (FALSE); + ldap_dnfree (dn); + + g_debug ("Got defaultNamingContext: %s", clo->default_naming_context); + + /* Next search for IPA field */ + clo->request = request_domain_info; + clo->result = NULL; + return TRUE; +} + +static gboolean +request_root_dse (EggTask *task, + Closure *clo, + LDAP *ldap) +{ + const char *attrs[] = { "defaultNamingContext" }; + + clo->request = NULL; + clo->result = result_root_dse; + + return search_ldap (task, clo, ldap, "", LDAP_SCOPE_BASE, NULL, attrs); +} + +static GIOCondition +on_ldap_io (LDAP *ldap, + GIOCondition cond, + gpointer user_data) +{ + EggTask *task = EGG_TASK (user_data); + Closure *clo = egg_task_get_task_data (task); + struct timeval tvpoll = { 0, 0 }; + LDAPMessage *message; + GError *error = NULL; + gboolean ret; + + /* Some failure */ + if (cond & G_IO_ERR) { + realm_ldap_set_error (&error, ldap, 0); + egg_task_return_error (task, error); + return G_IO_NVAL; + } + + /* Ready to get a result */ + if (cond & G_IO_IN && clo->result != NULL) { + switch (ldap_result (ldap, clo->msgid, 0, &tvpoll, &message)) { + case LDAP_RES_INTERMEDIATE: + case LDAP_RES_SEARCH_REFERENCE: + ret = TRUE; + break; + case -1: + realm_ldap_set_error (&error, ldap, -1); + egg_task_return_error (task, error); + ret = FALSE; + break; + case 0: + ret = TRUE; + break; + default: + ret = clo->result (task, clo, ldap, message); + ldap_msgfree (message); + break; + } + + if (!ret) + return G_IO_NVAL; + } + + if (cond & G_IO_OUT && clo->request != NULL) { + if (!(clo->request) (task, clo, ldap)) + return G_IO_NVAL; + } + + return (clo->request ? G_IO_OUT : 0) | + (clo->result ? G_IO_IN : 0); +} + +void +realm_disco_rootdse_async (GSocketAddress *address, + const gchar *explicit_server, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + EggTask *task; + Closure *clo; + + g_return_if_fail (address != NULL); + + task = egg_task_new (NULL, cancellable, callback, user_data); + clo = g_slice_new0 (Closure); + clo->disco = realm_disco_new (NULL); + clo->disco->explicit_server = g_strdup (explicit_server); + clo->request = request_root_dse; + egg_task_set_task_data (task, clo, closure_free); + + clo->source = realm_ldap_connect_anonymous (address, G_SOCKET_PROTOCOL_TCP, + cancellable); + g_source_set_callback (clo->source, (GSourceFunc)on_ldap_io, + g_object_ref (task), g_object_unref); + g_source_attach (clo->source, egg_task_get_context (task)); + + g_object_unref (task); +} + +RealmDisco * +realm_disco_rootdse_finish (GAsyncResult *result, + GError **error) +{ + Closure *clo; + RealmDisco *disco; + + g_return_val_if_fail (egg_task_is_valid (result, NULL), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (!egg_task_propagate_boolean (EGG_TASK (result), error)) + return FALSE; + + clo = egg_task_get_task_data (EGG_TASK (result)); + disco = clo->disco; + clo->disco = NULL; + + /* Should have been set above */ + g_return_val_if_fail (disco->domain_name, NULL); + + if (!disco->kerberos_realm) + disco->kerberos_realm = g_ascii_strup (disco->domain_name, -1); + + return disco; +} diff --git a/service/realm-disco-rootdse.h b/service/realm-disco-rootdse.h new file mode 100644 index 0000000..537d481 --- /dev/null +++ b/service/realm-disco-rootdse.h @@ -0,0 +1,31 @@ +/* realmd -- Realm configuration service + * + * Copyright 2013 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 + */ + +#ifndef __REALM_DISCO_ROOTDSE_H__ +#define __REALM_DISCO_ROOTDSE_H__ + +#include + +#include "realm-disco.h" + +void realm_disco_rootdse_async (GSocketAddress *address, + const gchar *explicit_server, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +RealmDisco * realm_disco_rootdse_finish (GAsyncResult *result, + GError **error); + +#endif /* __REALM_DISCO_ROOTDSE_H__ */ diff --git a/service/realm-disco.c b/service/realm-disco.c new file mode 100644 index 0000000..e650fa7 --- /dev/null +++ b/service/realm-disco.c @@ -0,0 +1,66 @@ +/* realmd -- Realm configuration service + * + * Copyright 2013 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-disco.h" + +GType +realm_disco_get_type (void) +{ + static GType type = 0; + + if (type == 0) + type = g_boxed_type_register_static ("RealmDisco", + (GBoxedCopyFunc)realm_disco_ref, + realm_disco_unref); + + return type; +} + +RealmDisco * +realm_disco_new (const gchar *domain) +{ + RealmDisco *disco; + + disco = g_slice_new0 (RealmDisco); + disco->refs = 1; + disco->domain_name = g_strdup (domain); + return disco; +} + +RealmDisco * +realm_disco_ref (RealmDisco *disco) +{ + g_return_val_if_fail (disco != NULL, NULL); + disco->refs++; + return disco; +} + +void +realm_disco_unref (gpointer data) +{ + RealmDisco *disco = data; + + if (!data) + return; + + if (disco->refs-- == 1) { + g_free (disco->domain_name); + g_free (disco->explicit_server); + g_free (disco->kerberos_realm); + g_free (disco->workgroup); + g_slice_free (RealmDisco, disco); + } +} diff --git a/service/realm-disco.h b/service/realm-disco.h new file mode 100644 index 0000000..73a61bf --- /dev/null +++ b/service/realm-disco.h @@ -0,0 +1,44 @@ +/* realmd -- Realm configuration service + * + * Copyright 2013 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 + */ + +#ifndef __REALM_DISCO_H__ +#define __REALM_DISCO_H__ + +#include +#include + +G_BEGIN_DECLS + +typedef struct { + gint refs; + const gchar *server_software; + gchar *domain_name; + gchar *kerberos_realm; + gchar *workgroup; + gchar *explicit_server; +} RealmDisco; + +#define REALM_TYPE_DISCO (realm_disco_get_type ()) + +GType realm_disco_get_type (void) G_GNUC_CONST; + +RealmDisco * realm_disco_new (const gchar *domain); + +RealmDisco * realm_disco_ref (RealmDisco *disco); + +void realm_disco_unref (gpointer disco); + +G_END_DECLS + +#endif /* __REALM_DISCO_H__ */ diff --git a/service/realm-discovery.c b/service/realm-discovery.c deleted file mode 100644 index f9400ad..0000000 --- a/service/realm-discovery.c +++ /dev/null @@ -1,108 +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-discovery.h" - -#include - -GHashTable * -realm_discovery_new (void) -{ - return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, - (GDestroyNotify)g_variant_unref); -} - -void -realm_discovery_add_string (GHashTable *discovery, - const gchar *type, - const gchar *value) -{ - g_return_if_fail (type != NULL); - g_return_if_fail (value != NULL); - - if (discovery != NULL) - realm_discovery_add_variant (discovery, type, g_variant_new_string (value)); -} - -const gchar * -realm_discovery_get_string (GHashTable *discovery, - const gchar *type) -{ - GVariant *variant; - - g_return_val_if_fail (type != NULL, NULL); - - if (discovery == NULL) - return NULL; - - variant = g_hash_table_lookup (discovery, type); - if (variant == NULL) - return NULL; - - 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, - GVariant *value) -{ - g_return_if_fail (type != NULL); - g_return_if_fail (value != NULL); - - if (discovery != NULL) - g_hash_table_insert (discovery, g_strdup (type), g_variant_ref_sink (value)); -} - -void -realm_discovery_add_strings (GHashTable *discovery, - const gchar *type, - const char **value) -{ - g_return_if_fail (type != NULL); - g_return_if_fail (value != NULL); - - if (discovery != NULL) - realm_discovery_add_variant (discovery, type, g_variant_new_strv (value, -1)); -} - -const gchar ** -realm_discovery_get_strings (GHashTable *discovery, - const gchar *type) -{ - GVariant *variant; - - g_return_val_if_fail (type != NULL, NULL); - - if (discovery == NULL) - return NULL; - - variant = g_hash_table_lookup (discovery, type); - if (variant == NULL) - return NULL; - - return g_variant_get_strv (variant, NULL); -} diff --git a/service/realm-discovery.h b/service/realm-discovery.h deleted file mode 100644 index 4f35c86..0000000 --- a/service/realm-discovery.h +++ /dev/null @@ -1,50 +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_DISCOVERY_H__ -#define __REALM_DISCOVERY_H__ - -#include - -G_BEGIN_DECLS - -GHashTable * realm_discovery_new (void); - -void realm_discovery_add_string (GHashTable *discovery, - const gchar *type, - const gchar *value); - -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); - -void realm_discovery_add_strings (GHashTable *discovery, - const gchar *type, - const char **strings); - -const gchar ** realm_discovery_get_strings (GHashTable *discovery, - const gchar *type); - -G_END_DECLS - -#endif /* __REALM_AD_DISCOVER_H__ */ diff --git a/service/realm-example-provider.c b/service/realm-example-provider.c index c90a32c..3ef74a4 100644 --- a/service/realm-example-provider.c +++ b/service/realm-example-provider.c @@ -18,7 +18,7 @@ #include "realm-daemon.h" #include "realm-dbus-constants.h" #include "realm-diagnostics.h" -#include "realm-discovery.h" +#include "realm-disco.h" #include "realm-errors.h" #include "realm-example.h" #include "realm-example-provider.h" @@ -182,7 +182,7 @@ realm_example_provider_discover_async (RealmProvider *provider, domain = NULL; } - g_object_set_data_full (G_OBJECT ("task"), "the-domain", domain, g_free); + g_object_set_data_full (G_OBJECT (task), "the-domain", domain, g_free); realm_usleep_async (delay * G_USEC_PER_SEC, realm_invocation_get_cancellable (invocation), diff --git a/service/realm-ipa-discover.c b/service/realm-ipa-discover.c deleted file mode 100644 index 484e65b..0000000 --- a/service/realm-ipa-discover.c +++ /dev/null @@ -1,611 +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-ipa-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-invocation.h" -#include "realm-network.h" - -#include - -typedef struct { - GObject parent; - GDBusMethodInvocation *invocation; - gboolean completed; - gboolean found_ipa; - GError *error; - GAsyncReadyCallback callback; - gpointer user_data; - - GSrvTarget *kdc; - GBytes *http_request; - GIOStream *current_connection; - GTlsCertificate *peer_certificate; - GSocketConnectable *peer_identity; -} RealmIpaDiscover; - -typedef struct { - GObjectClass parent; -} RealmIpaDiscoverClass; - -#define REALM_TYPE_IPA_DISCOVER (realm_ipa_discover_get_type ()) -#define REALM_IPA_DISCOVER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), REALM_TYPE_IPA_DISCOVER, RealmIpaDiscover)) -#define REALM_IS_IPA_DISCOVER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), REALM_TYPE_IPA_DISCOVER)) - -GType realm_ipa_discover_get_type (void) G_GNUC_CONST; - -void realm_ipa_discover_async_result_init (GAsyncResultIface *iface); - -G_DEFINE_TYPE_WITH_CODE (RealmIpaDiscover, realm_ipa_discover, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, realm_ipa_discover_async_result_init); -); - -static void -realm_ipa_discover_init (RealmIpaDiscover *self) -{ - -} - -static void -realm_ipa_discover_finalize (GObject *obj) -{ - RealmIpaDiscover *self = REALM_IPA_DISCOVER (obj); - - g_object_unref (self->invocation); - g_clear_error (&self->error); - g_srv_target_free (self->kdc); - - if (self->http_request) - g_bytes_unref (self->http_request); - - if (self->current_connection) - g_object_unref (self->current_connection); - - if (self->peer_certificate) - g_object_unref (self->peer_certificate); - - if (self->peer_identity) - g_object_unref (self->peer_identity); - - g_assert (self->callback == NULL); - - G_OBJECT_CLASS (realm_ipa_discover_parent_class)->finalize (obj); -} - -static void -realm_ipa_discover_class_init (RealmIpaDiscoverClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = realm_ipa_discover_finalize; -} - -static GObject * -realm_ipa_discover_get_source_object (GAsyncResult *result) -{ - return g_object_ref (result); -} - -static gpointer -realm_ipa_discover_get_user_data (GAsyncResult *result) -{ - /* What does this do? */ - g_return_val_if_reached (NULL); -} - -void -realm_ipa_discover_async_result_init (GAsyncResultIface *iface) -{ - iface->get_source_object = realm_ipa_discover_get_source_object; - iface->get_user_data = realm_ipa_discover_get_user_data; -} - -/* TODO: This stuff will shortly be part of glib */ - -typedef struct { - GCancellable *cancellable; - guchar *buf; - gsize count; - gsize nread; -} ReadAllClosure; - -static void -read_all_closure_free (gpointer data) -{ - ReadAllClosure *closure = data; - if (closure->cancellable) - g_object_unref (closure->cancellable); - g_free (closure->buf); - g_slice_free (ReadAllClosure, closure); -} - -static void -read_all_callback (GObject *stream, - GAsyncResult *result, - gpointer user_data) -{ - GSimpleAsyncResult *simple = user_data; - ReadAllClosure *closure = g_simple_async_result_get_op_res_gpointer (simple); - - GError *error = NULL; - gssize nread; - - nread = g_input_stream_read_finish (G_INPUT_STREAM (stream), - result, &error); - if (nread == -1) - { - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete (simple); - } - else if (nread == 0) - { - g_simple_async_result_complete (simple); - } - else - { - closure->nread += nread; - if (closure->count > closure->nread) - { - g_input_stream_read_async (G_INPUT_STREAM (stream), - closure->buf + closure->nread, - closure->count - closure->nread, - G_PRIORITY_DEFAULT, closure->cancellable, - read_all_callback, g_object_ref (simple)); - } - else - { - g_simple_async_result_complete (simple); - } - } - - g_object_unref (simple); -} - -static void -read_all_bytes_async (GInputStream *stream, - gsize count, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - ReadAllClosure *closure; - - simple = g_simple_async_result_new (G_OBJECT (stream), - callback, user_data, - read_all_bytes_async); - closure = g_slice_new0 (ReadAllClosure); - closure->buf = g_malloc (count); - closure->count = count; - closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL; - g_simple_async_result_set_op_res_gpointer (simple, closure, read_all_closure_free); - - g_input_stream_read_async (stream, closure->buf, count, - G_PRIORITY_DEFAULT, cancellable, - read_all_callback, simple); -} - -static GBytes * -read_all_bytes_finish (GInputStream *stream, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - ReadAllClosure *closure; - GBytes *bytes; - - simple = G_SIMPLE_ASYNC_RESULT (result); - closure = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - - bytes = g_bytes_new_take (closure->buf, closure->nread); - closure->buf = NULL; - closure->nread = 0; - - return bytes; -} - -typedef struct { - GCancellable *cancellable; - GBytes *bytes; - gsize written; -} WriteAllClosure; - -static void -write_all_closure_free (gpointer data) -{ - WriteAllClosure *closure = data; - if (closure->cancellable) - g_object_unref (closure->cancellable); - g_bytes_unref (closure->bytes); - g_slice_free (WriteAllClosure, closure); -} - -static void -write_all_callback (GObject *stream, - GAsyncResult *result, - gpointer user_data) -{ - GSimpleAsyncResult *simple = user_data; - WriteAllClosure *closure = g_simple_async_result_get_op_res_gpointer (simple); - GError *error = NULL; - const guchar *data; - gsize size; - gssize nwrote; - - nwrote = g_output_stream_write_finish (G_OUTPUT_STREAM (stream), - result, &error); - if (nwrote < 0) - { - g_simple_async_result_take_error (simple, error); - } - else - { - closure->written += nwrote; - data = g_bytes_get_data (closure->bytes, &size); - if (closure->written < size) - { - g_output_stream_write_async (G_OUTPUT_STREAM (stream), - data + closure->written, - size - closure->written, - G_PRIORITY_DEFAULT, - closure->cancellable, - write_all_callback, - g_object_ref (simple)); - } - else - { - g_simple_async_result_complete (simple); - } - } - - g_object_unref (simple); -} - -static void -write_all_bytes_async (GOutputStream *stream, - GBytes *bytes, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - WriteAllClosure *closure; - gsize size; - gconstpointer data; - - data = g_bytes_get_data (bytes, &size); - simple = g_simple_async_result_new (G_OBJECT (stream), - callback, user_data, - write_all_bytes_async); - closure = g_slice_new0 (WriteAllClosure); - closure->bytes = g_bytes_ref (bytes); - closure->cancellable = cancellable ? g_object_ref (cancellable) : cancellable; - g_simple_async_result_set_op_res_gpointer (simple, closure, write_all_closure_free); - - g_output_stream_write_async (stream, - data, size, - G_PRIORITY_DEFAULT, - cancellable, - write_all_callback, - simple); -} - -static gboolean -write_all_bytes_finish (GOutputStream *stream, - GAsyncResult *result, - GError **error) -{ - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) - return FALSE; - return TRUE; -} - -static void -ipa_discover_complete (RealmIpaDiscover *self) -{ - GAsyncReadyCallback call; - gpointer user_data; - - g_assert (!self->completed); - self->completed = TRUE; - call = self->callback; - user_data = self->user_data; - self->callback = NULL; - self->user_data = NULL; - - if (call != NULL) - (call) (NULL, G_ASYNC_RESULT (self), user_data); - - /* Matches g_object_new in realm_ipa_discover_async */ - g_object_unref (self); -} - -static void -ipa_discover_take_error (RealmIpaDiscover *self, - const gchar *message, - GError *error) -{ - g_assert (!self->completed); - - realm_diagnostics_error (self->invocation, error, "%s", message); - - /* We get the first as our operation error */ - if (self->error) - g_error_free (error); - else - self->error = error; -} - -static GBytes * -strip_http_header (GBytes *bytes) -{ - gchar *data; - const gchar *contents; - gsize length; - const gchar *br; - - /* Turn this into a string */ - data = g_bytes_unref_to_data (bytes, &length); - data = g_realloc (data, length + 1); - data[length] = 0; - - br = strstr (data, "\r\n\r\n"); - if (br == NULL) { - br = strstr (data, "\n\n"); - if (br == NULL) { - contents = NULL; - } else { - contents = br + 2; - } - } else { - contents = br + 4; - } - - if (contents == NULL) { - return g_bytes_new_static ("", 0); - - } else { - return g_bytes_new_with_free_func (contents, - length - (contents - data), - g_free, data); - } -} - -static void -on_read_http_response (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - RealmIpaDiscover *self = REALM_IPA_DISCOVER (user_data); - GTlsCertificate *certificate; - GError *error = NULL; - const gchar *data; - gsize length; - GBytes *bytes; - - bytes = read_all_bytes_finish (G_INPUT_STREAM (source), result, &error); - - if (!self->peer_certificate || !self->peer_identity) { - g_debug ("No peer certificate or peer identity received."); - - } else if (error == NULL) { - bytes = strip_http_header (bytes); - data = g_bytes_get_data (bytes, &length); - certificate = g_tls_certificate_new_from_pem (data, length, &error); - g_bytes_unref (bytes); - - if (certificate) { - - /* - * We're not verifying this certificate for security purposes - * but to check that this is a real IPA server. The CA certificate - * should be the anchor for the peer certificate. - */ - if (g_tls_certificate_verify (self->peer_certificate, - self->peer_identity, - certificate) == 0) { - realm_diagnostics_info (self->invocation, "Retrieved IPA CA certificate verifies the HTTPS connection"); - self->found_ipa = TRUE; - ipa_discover_complete (self); - - } else { - realm_diagnostics_info (self->invocation, "Retrieved IPA CA certificate does not verify the HTTPS connection"); - } - - g_object_unref (certificate); - } - } - - if (error != NULL) - ipa_discover_take_error (self, "Couldn't read certificate via HTTP", error); - if (!self->completed) - ipa_discover_complete (self); - - g_object_unref (self); -} - - -static void -on_write_http_request (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - RealmIpaDiscover *self = REALM_IPA_DISCOVER (user_data); - GCancellable *cancellable; - GError *error = NULL; - GInputStream *input; - - /* TODO: Update to new bytes interface */ - - write_all_bytes_finish (G_OUTPUT_STREAM (source), result, &error); - if (error == NULL) { - input = g_io_stream_get_input_stream (G_IO_STREAM (self->current_connection)); - cancellable = realm_invocation_get_cancellable (self->invocation); - read_all_bytes_async (input, 100 * 1024, cancellable, - on_read_http_response, g_object_ref (self)); - } else { - ipa_discover_take_error (self, "Couldn't send HTTP request for certificate", error); - ipa_discover_complete (self); - } - - g_object_unref (self); -} - -static void -on_connect_to_host (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - RealmIpaDiscover *self = REALM_IPA_DISCOVER (user_data); - gchar *request; - GCancellable *cancellable; - GSocketConnection *connection; - GOutputStream *output; - GError *error = NULL; - - g_assert (self->current_connection == NULL); - g_assert (self->http_request == NULL); - - request = g_strdup_printf ("GET /ipa/config/ca.crt HTTP/1.0\r\n" - "Host: %s\r\n" - "\r\n", g_srv_target_get_hostname (self->kdc)); - self->http_request = g_bytes_new_take (request, strlen (request)); - - connection = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (source), result, &error); - if (error == NULL) { - self->current_connection = G_IO_STREAM (connection); - output = g_io_stream_get_output_stream (self->current_connection); - cancellable = realm_invocation_get_cancellable (self->invocation); - write_all_bytes_async (output, self->http_request, cancellable, - on_write_http_request, g_object_ref (self)); - - /* Errors that mean no domain discovered */ - } else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED) || - g_error_matches (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE) || - g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NETWORK_UNREACHABLE) || - g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) { - g_debug ("Couldn't connect to check for IPA domain: %s", error->message); - ipa_discover_complete (self); - - } else { - ipa_discover_take_error (self, "Couldn't connect to check for IPA domain", error); - ipa_discover_complete (self); - } - - g_object_unref (self); -} - -static void -on_connection_event (GSocketClient *client, - GSocketClientEvent event, - GSocketConnectable *connectable, - GIOStream *connection, - gpointer user_data) -{ - RealmIpaDiscover *self = REALM_IPA_DISCOVER (user_data); - - if (event == G_SOCKET_CLIENT_TLS_HANDSHAKED) { - g_return_if_fail (self->peer_certificate == NULL); - self->peer_certificate = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (connection)); - if (self->peer_certificate) - g_object_ref (self->peer_certificate); - - } else if (event == G_SOCKET_CLIENT_RESOLVED) { - if (!self->peer_identity) - self->peer_identity = g_object_ref (connectable); - - /* Once connected, raise the timeout, so TLS can succeed */ - } else if (event == G_SOCKET_CLIENT_CONNECTED) { - g_socket_client_set_timeout (client, 30); - } -} - -void -realm_ipa_discover_async (GSrvTarget *kdc, - GDBusMethodInvocation *invocation, - GAsyncReadyCallback callback, - gpointer user_data) -{ - RealmIpaDiscover *self; - GCancellable *cancellable; - GSocketClient *client; - const gchar *hostname; - - g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation)); - - cancellable = realm_invocation_get_cancellable (invocation); - self = g_object_new (REALM_TYPE_IPA_DISCOVER, NULL); - self->invocation = g_object_ref (invocation); - self->callback = callback; - self->user_data = user_data; - self->kdc = g_srv_target_copy (kdc); - - hostname = g_srv_target_get_hostname (self->kdc); - client = g_socket_client_new (); - - /* Initial socket connections are limited to a low timeout*/ - g_socket_client_set_timeout (client, 5); - - /* - * Note that we accept invalid certificates, we're just comparing them - * with what's on the server at this point. Later during the join the - * certificate is used correctly. - */ - - g_socket_client_set_tls (client, TRUE); - g_socket_client_set_tls_validation_flags (client, 0); - - g_signal_connect_data (client, "event", G_CALLBACK (on_connection_event), - g_object_ref (self), (GClosureNotify)g_object_unref, - G_CONNECT_AFTER); - - realm_diagnostics_info (self->invocation, "Trying to retrieve IPA certificate from %s", hostname); - - g_socket_client_connect_to_host_async (client, hostname, 443, cancellable, - on_connect_to_host, g_object_ref (self)); - - g_object_unref (client); - - /* self is released in ipa_discover_complete */ -} - -gboolean -realm_ipa_discover_finish (GAsyncResult *result, - GError **error) -{ - RealmIpaDiscover *self; - - 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); - - /* A failure */ - if (self->error) { - if (error) - *error = g_error_copy (self->error); - return FALSE; - } - - return self->found_ipa; -} diff --git a/service/realm-ipa-discover.h b/service/realm-ipa-discover.h deleted file mode 100644 index 6da1b99..0000000 --- a/service/realm-ipa-discover.h +++ /dev/null @@ -1,34 +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_IPA_DISCOVER_H__ -#define __REALM_IPA_DISCOVER_H__ - -#include - -G_BEGIN_DECLS - -void realm_ipa_discover_async (GSrvTarget *kdc, - GDBusMethodInvocation *invocation, - GAsyncReadyCallback callback, - gpointer user_data); - -gboolean realm_ipa_discover_finish (GAsyncResult *result, - GError **error); - -G_END_DECLS - -#endif /* __REALM_IPA_DISCOVER_H__ */ diff --git a/service/realm-kerberos-discover.c b/service/realm-kerberos-discover.c deleted file mode 100644 index c9ca93c..0000000 --- a/service/realm-kerberos-discover.c +++ /dev/null @@ -1,566 +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-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-invocation.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; - gint outstanding_kerberos; - gboolean found_msdcs; - gint outstanding_msdcs; - gboolean started_ipa; - gboolean found_ipa; - gint outstanding_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; - -static void maybe_complete_discover (RealmKerberosDiscover *self); - -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_call_callbacks (RealmKerberosDiscover *self) -{ - Callback *call, *next; - - 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; - } -} - -static void -kerberos_discover_complete (RealmKerberosDiscover *self) -{ - g_object_ref (self); - - g_assert (!self->completed); - self->completed = TRUE; - kerberos_discover_call_callbacks (self); - g_object_unref (self); -} - -static void -on_discover_ipa (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - RealmKerberosDiscover *self = REALM_KERBEROS_DISCOVER (user_data); - GError *error = NULL; - - g_assert (self->outstanding_ipa > 0); - self->outstanding_ipa--; - - if (!self->completed && !self->found_ipa) { - self->found_ipa = realm_ipa_discover_finish (result, &error); - - /* - * No errors from the IPA discovery are treated as discovery - * failures, but merely the abscence of IPA. Unless explicitly - * cancelled. - */ - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - g_clear_error (&self->error); - self->error = error; - - } else { - realm_diagnostics_error (self->key.invocation, error, - "Couldn't discover IPA KDC"); - g_clear_error (&error); - } - - maybe_complete_discover (self); - } - - g_object_unref (self); -} - -static void -maybe_complete_discover (RealmKerberosDiscover *self) -{ - GDBusMethodInvocation *invocation = self->key.invocation; - GList *l; - gint i; - - /* If still discovering whether kerberos, then not complete */ - if (self->outstanding_kerberos) - return; - - /* If found either IPA or AD then finish regardless of other check */ - if (!self->found_msdcs && !self->found_ipa) { - if (!self->outstanding_msdcs && !self->started_ipa) { - self->started_ipa = TRUE; - - /* If domain is not AD, start IPA discovery, for first N KDCs */ - for (l = self->servers, i = 0; l != NULL && i < 3; l = g_list_next (l), i++) { - self->outstanding_ipa++; - realm_ipa_discover_async (l->data, invocation, - on_discover_ipa, g_object_ref (self)); - } - } - - if (self->outstanding_msdcs || self->outstanding_ipa) - 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_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; - - self->outstanding_kerberos = 0; - - 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' or 'temporarily unable to resolve' as errors */ - if (g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND) || - g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE)) - 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); - - } else { - realm_diagnostics_error (invocation, error, "Couldn't lookup SRV records for domain"); - g_clear_error (&self->error); - self->error = error; - } - - 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; - - self->outstanding_msdcs = 0; - - if (self->completed) { - g_object_unref (self); - return; - } - - records = g_resolver_lookup_service_finish (resolver, result, &error); - - /* We don't treat 'host not found' or 'temporarily unable to resolve' as errors */ - if (g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND) || - g_error_matches (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE)) - g_clear_error (&error); - - if (error == NULL) { - self->found_msdcs = (records != NULL); - g_list_free_full (records, (GDestroyNotify)g_srv_target_free); - - } else { - realm_diagnostics_error (invocation, error, "Failure to lookup domain MSDCS records"); - g_clear_error (&self->error); - self->error = error; - } - - maybe_complete_discover (self); - g_object_unref (self); -} - -static void -kerberos_discover_domain_begin (RealmKerberosDiscover *self) -{ - GDBusMethodInvocation *invocation = self->key.invocation; - GCancellable *cancellable; - GResolver *resolver; - gchar *msdcs; - - g_assert (self->domain != NULL); - - cancellable = realm_invocation_get_cancellable (invocation); - - 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, cancellable, - on_resolve_kerberos, g_object_ref (self)); - self->outstanding_kerberos = 1; - - /* 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, cancellable, - on_resolve_msdcs, g_object_ref (self)); - self->outstanding_msdcs = 1; - - 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_invocation_get_key (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_invocation_get_key (k1->invocation), - realm_invocation_get_key (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_call_callbacks (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; - GPtrArray *servers; - gchar *realm; - gchar *name; - GList *l; - - 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); - name = g_ascii_strdown (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 KDCs */ - self->servers = g_srv_target_list_sort (self->servers); - - servers = g_ptr_array_new (); - for (l = self->servers; l != NULL; l = g_list_next (l)) - g_ptr_array_add (servers, (void *)g_srv_target_get_hostname (l->data)); - g_ptr_array_add (servers, NULL); - realm_discovery_add_strings (*discovery, REALM_DBUS_DISCOVERY_KDCS, - (const gchar **)servers->pdata); - g_ptr_array_free (servers, TRUE); - - /* 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); - } - } - - g_free (realm); - return name; -} diff --git a/service/realm-kerberos-discover.h b/service/realm-kerberos-discover.h deleted file mode 100644 index 913726f..0000000 --- a/service/realm-kerberos-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_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-kerberos-provider.c b/service/realm-kerberos-provider.c index de37e31..2e3bf7f 100644 --- a/service/realm-kerberos-provider.c +++ b/service/realm-kerberos-provider.c @@ -16,8 +16,7 @@ #include "egg-task.h" #include "realm-dbus-constants.h" -#include "realm-discovery.h" -#include "realm-kerberos-discover.h" +#include "realm-invocation.h" #include "realm-kerberos-provider.h" #include @@ -46,7 +45,24 @@ on_kerberos_discover (GObject *source, gpointer user_data) { EggTask *task = EGG_TASK (user_data); - egg_task_return_pointer (task, g_object_ref (result), g_object_unref); + const gchar *domain = egg_task_get_task_data (task); + GError *error = NULL; + RealmDisco *disco; + GList *targets; + + targets = g_resolver_lookup_service_finish (G_RESOLVER (source), result, &error); + if (targets) { + g_list_free_full (targets, (GDestroyNotify)g_srv_target_free); + disco = realm_disco_new (domain); + disco->kerberos_realm = g_ascii_strup (domain, -1); + egg_task_return_pointer (task, disco, realm_disco_unref); + + } else if (error) { + g_debug ("Resolving %s failed: %s", domain, error->message); + g_error_free (error); + egg_task_return_pointer (task, NULL, NULL); + } + g_object_unref (task); } @@ -60,6 +76,8 @@ realm_kerberos_provider_discover_async (RealmProvider *provider, { EggTask *task; const gchar *software; + GResolver *resolver; + gchar *name; task = egg_task_new (provider, NULL, callback, user_data); @@ -69,8 +87,13 @@ realm_kerberos_provider_discover_async (RealmProvider *provider, egg_task_return_pointer (task, NULL, NULL); } else { - realm_kerberos_discover_async (string, invocation, on_kerberos_discover, - g_object_ref (task)); + name = g_hostname_to_ascii (string); + resolver = g_resolver_get_default (); + g_resolver_lookup_service_async (resolver, "kerberos", "udp", name, + realm_invocation_get_cancellable (invocation), + on_kerberos_discover, g_object_ref (task)); + egg_task_set_task_data (task, name, g_free); + g_object_unref (resolver); } g_object_unref (task); @@ -83,24 +106,17 @@ realm_kerberos_provider_discover_finish (RealmProvider *provider, GError **error) { RealmKerberos *realm = NULL; - GHashTable *discovery; - GAsyncResult *kerberos_result; - gchar *name; - - kerberos_result = egg_task_propagate_pointer (EGG_TASK (result), error); - if (kerberos_result == NULL) - return NULL; + RealmDisco *disco; - name = realm_kerberos_discover_finish (kerberos_result, &discovery, error); - if (name == NULL) + disco = egg_task_propagate_pointer (EGG_TASK (result), error); + if (disco == NULL) return NULL; realm = realm_provider_lookup_or_register_realm (provider, REALM_TYPE_KERBEROS, - name, discovery); + disco->domain_name, disco); - g_free (name); - g_hash_table_unref (discovery); + realm_disco_unref (disco); if (realm == NULL) return NULL; diff --git a/service/realm-kerberos.c b/service/realm-kerberos.c index 04c7f30..5a0eeec 100644 --- a/service/realm-kerberos.c +++ b/service/realm-kerberos.c @@ -20,7 +20,7 @@ #include "realm-dbus-constants.h" #include "realm-dbus-generated.h" #include "realm-diagnostics.h" -#include "realm-discovery.h" +#include "realm-disco.h" #include "realm-errors.h" #include "realm-invocation.h" #include "realm-kerberos.h" @@ -43,7 +43,7 @@ #include struct _RealmKerberosPrivate { - GHashTable *discovery; + RealmDisco *disco; RealmDbusRealm *realm_iface; RealmDbusKerberos *kerberos_iface; RealmDbusKerberosMembership *membership_iface; @@ -52,7 +52,7 @@ struct _RealmKerberosPrivate { enum { PROP_0, PROP_NAME, - PROP_DISCOVERY, + PROP_DISCO, PROP_PROVIDER, }; @@ -523,7 +523,6 @@ realm_kerberos_constructed (GObject *obj) RealmKerberos *self = REALM_KERBEROS (obj); const gchar *supported_interfaces[3]; GVariant *supported; - const gchar *name; G_OBJECT_CLASS (realm_kerberos_parent_class)->constructed (obj); @@ -555,13 +554,11 @@ realm_kerberos_constructed (GObject *obj) realm_dbus_realm_set_supported_interfaces (self->pv->realm_iface, supported_interfaces); - if (self->pv->discovery) { - name = realm_discovery_get_string (self->pv->discovery, REALM_DBUS_DISCOVERY_DOMAIN); - if (name) - realm_kerberos_set_domain_name (self, name); - name = realm_discovery_get_string (self->pv->discovery, REALM_DBUS_DISCOVERY_REALM); - if (name) - realm_kerberos_set_realm_name (self, name); + if (self->pv->disco) { + if (self->pv->disco->domain_name) + realm_kerberos_set_domain_name (self, self->pv->disco->domain_name); + if (self->pv->disco->kerberos_realm) + realm_kerberos_set_realm_name (self, self->pv->disco->kerberos_realm); } } @@ -577,8 +574,8 @@ realm_kerberos_get_property (GObject *obj, case PROP_NAME: g_value_set_string (value, realm_kerberos_get_name (self)); break; - case PROP_DISCOVERY: - g_value_set_boxed (value, realm_kerberos_get_discovery (self)); + case PROP_DISCO: + g_value_set_boxed (value, realm_kerberos_get_disco (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); @@ -599,8 +596,8 @@ realm_kerberos_set_property (GObject *obj, realm_dbus_realm_set_name (self->pv->realm_iface, g_value_get_string (value)); break; - case PROP_DISCOVERY: - realm_kerberos_set_discovery (self, g_value_get_boxed (value)); + case PROP_DISCO: + realm_kerberos_set_disco (self, g_value_get_boxed (value)); break; case PROP_PROVIDER: /* ignore */ @@ -621,8 +618,8 @@ realm_kerberos_finalize (GObject *obj) if (self->pv->membership_iface) g_object_unref (self->pv->membership_iface); - if (self->pv->discovery) - g_hash_table_unref (self->pv->discovery); + if (self->pv->disco) + realm_disco_unref (self->pv->disco); G_OBJECT_CLASS (realm_kerberos_parent_class)->finalize (obj); } @@ -646,9 +643,9 @@ realm_kerberos_class_init (RealmKerberosClass *klass) g_param_spec_string ("name", "Name", "Name", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, PROP_DISCOVERY, - g_param_spec_boxed ("discovery", "Discovery", "Discovery Data", - G_TYPE_HASH_TABLE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_DISCO, + g_param_spec_boxed ("disco", "Discovery", "Discovery Data", + REALM_TYPE_DISCO, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_PROVIDER, g_param_spec_object ("provider", "Provider", "Realm Provider", @@ -656,24 +653,23 @@ realm_kerberos_class_init (RealmKerberosClass *klass) } void -realm_kerberos_set_discovery (RealmKerberos *self, - GHashTable *discovery) +realm_kerberos_set_disco (RealmKerberos *self, + RealmDisco *disco) { g_return_if_fail (REALM_IS_KERBEROS (self)); - if (discovery) - g_hash_table_ref (discovery); - if (self->pv->discovery) - g_hash_table_unref (self->pv->discovery); - self->pv->discovery = discovery; - g_object_notify (G_OBJECT (self), "discovery"); + if (disco) + realm_disco_ref (disco); + realm_disco_unref (self->pv->disco); + self->pv->disco = disco; + g_object_notify (G_OBJECT (self), "disco"); } -GHashTable * -realm_kerberos_get_discovery (RealmKerberos *self) +RealmDisco * +realm_kerberos_get_disco (RealmKerberos *self) { g_return_val_if_fail (REALM_IS_KERBEROS (self), NULL); - return self->pv->discovery; + return self->pv->disco; } gchar ** diff --git a/service/realm-kerberos.h b/service/realm-kerberos.h index 0480686..30bb75b 100644 --- a/service/realm-kerberos.h +++ b/service/realm-kerberos.h @@ -22,6 +22,7 @@ #include #include "realm-dbus-generated.h" +#include "realm-disco.h" G_BEGIN_DECLS @@ -68,10 +69,10 @@ struct _RealmKerberosClass { GType realm_kerberos_get_type (void) G_GNUC_CONST; -void realm_kerberos_set_discovery (RealmKerberos *self, - GHashTable *discovery); +void realm_kerberos_set_disco (RealmKerberos *self, + RealmDisco *disco); -GHashTable * realm_kerberos_get_discovery (RealmKerberos *self); +RealmDisco * realm_kerberos_get_disco (RealmKerberos *self); gchar ** realm_kerberos_parse_logins (RealmKerberos *self, gboolean lower, diff --git a/service/realm-ldap.c b/service/realm-ldap.c new file mode 100644 index 0000000..09c8002 --- /dev/null +++ b/service/realm-ldap.c @@ -0,0 +1,357 @@ +/* realmd -- Realm configuration service + * + * Copyright 2013 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 "egg-task.h" +#include "realm-ldap.h" + +#include +#include + +#include +#include + +#include + +#include + +/* + * So the reason that we don't use GSocket is because its fd's are always + * non-blocking internally. We can't just go and hand these non-blocking + * fds to openldap, which then fiddles with blocking state. + */ + +GQuark +realm_ldap_error_get_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("realm-ldap-error"); + return quark; +} + +typedef struct { + GSource source; + LDAP *ldap; + GPollFD pollfd; + GIOCondition condition; + GCancellable *cancellable; + GPollFD cancel_pollfd; + + /* Whether connect() was called */ + gboolean connect_begun; + + /* An LDAP failure we should always return if non-zero */ + int force_fail; + + /* Used when binding */ + int bind_msg; + gboolean bind_done; +} LdapSource; + +static gboolean +ldap_source_prepare (GSource *source, + gint *timeout) +{ + LdapSource *ls = (LdapSource *)source; + + if (!ls->connect_begun || ls->force_fail != 0) + return TRUE; + if (g_cancellable_is_cancelled (ls->cancellable)) + return TRUE; + + *timeout = -1; + if ((ls->condition & ls->pollfd.revents) != 0) + return TRUE; + + ls->pollfd.events = ls->condition; + return FALSE; +} + +static gboolean +ldap_source_check (GSource *source) +{ + int timeout; + return ldap_source_prepare (source, &timeout); +} + +static void +ldap_set_result_code (LDAP *ldap, + int rc) +{ + int check; + + if (ldap_get_option (ldap, LDAP_OPT_RESULT_CODE, &check) < 0 || check != rc) { + ldap_set_option (ldap, LDAP_OPT_RESULT_CODE, &rc); + ldap_set_option (ldap, LDAP_OPT_DIAGNOSTIC_MESSAGE, NULL); + } +} + +static int +ldap_get_result_code (LDAP *ldap) +{ + int rc; + if (ldap_get_option (ldap, LDAP_OPT_RESULT_CODE, &rc) < 0) + g_return_val_if_reached (LDAP_LOCAL_ERROR); + return rc; +} + + +static gboolean +ldap_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + RealmLdapCallback func = (RealmLdapCallback)callback; + LdapSource *ls = (LdapSource *)source; + LDAPMessage *message; + GIOCondition cond; + struct berval cred; + Sockbuf *sb = NULL; + ber_socket_t sock; + int rc; + + cond = ls->pollfd.revents & ls->condition; + + /* + * We report cancels as an error. The callback can check if it + * was cancelled by looking for LDAP_CANCELLED result code. + */ + if (g_cancellable_is_cancelled (ls->cancellable)) { + ls->force_fail = LDAP_CANCELLED; + + /* + * We fire off an anonymous bind which starts the connection. + * In the case of UDP the bind becomes a no-op, and in the + * case of TCP, anonymous binds are what we want in realmd + * + * Note that we don't wait for G_IO_OUT to do this. The very + * act of doing the bind request starts the connection. + */ + } else if (ls->force_fail == 0 && ls->bind_msg == 0) { + + /* An anonymous bind looks like this */ + cred.bv_val = ""; + cred.bv_len = 0; + rc = ldap_sasl_bind (ls->ldap, NULL, LDAP_SASL_SIMPLE, &cred, NULL, NULL, + &ls->bind_msg); + + /* + * Because we're connecting asyncronously LDAP_X_CONNECTING means + * that connect() has not yet completed, need to call ldap_sasl_bind + * again at a later time. + */ + if (rc == LDAP_X_CONNECTING || rc == LDAP_SUCCESS) { + + /* + * Now that we've called ldap_sasl_bind at least once, the LDAP + * should have a socket. Pull it out and start watching it. + */ + if (!ls->connect_begun) { + ls->connect_begun = TRUE; + if (ldap_get_option (ls->ldap, LDAP_OPT_SOCKBUF, &sb) < 0) + g_return_val_if_reached (FALSE); + g_return_val_if_fail (sb != NULL, FALSE); + if (ber_sockbuf_ctrl (sb, LBER_SB_OPT_GET_FD, &sock) != 1) + g_return_val_if_reached (FALSE); + ls->pollfd.fd = sock; + ls->pollfd.events = ls->condition; + ls->pollfd.revents = 0; + g_source_add_poll (source, &ls->pollfd); + } + + if (rc == LDAP_X_CONNECTING) { + ls->bind_msg = 0; + return TRUE; /* Wait, and call ldap_sasl_bind() again */ + } + + /* Sending the bind request failed */ + } else { + ls->force_fail = rc; + } + } + + /* + * Check for bind response + */ + if (ls->force_fail == 0 && ls->bind_msg != 0 && !ls->bind_done) { + struct timeval tv = { 0, 0 }; + rc = ldap_result (ls->ldap, ls->bind_msg, 0, &tv, &message); + + /* ldap_result() failed, pretty unexpected */ + if (rc < 0) { + ls->force_fail = ldap_get_result_code (ls->ldap); + + /* No bind result yet, wait and call ldap_result() again later */ + } else if (rc == 0) { + return TRUE; + + /* A bind result, yay */ + } else { + ls->bind_done = TRUE; + + /* Figure out whether bind was successful or not */ + rc = ldap_parse_result (ls->ldap, message, NULL, NULL, NULL, NULL, NULL, 1); + if (rc < 0) + ls->force_fail = rc; + else + ls->force_fail = ldap_get_result_code (ls->ldap); + } + } + + if (ls->force_fail != 0) { + ldap_set_result_code (ls->ldap, ls->force_fail); + cond |= G_IO_ERR; + } + + if (func != NULL && cond != 0) { + cond = (* func) (ls->ldap, cond, user_data); + if ((cond & G_IO_NVAL) == G_IO_NVAL) + return FALSE; + cond |= G_IO_HUP | G_IO_ERR; + ls->condition = cond; + } + + return TRUE; +} + +static void +ldap_source_finalize (GSource *source) +{ + LdapSource *ldap_source = (LdapSource *)source; + + ldap_unbind_ext (ldap_source->ldap, NULL, NULL); + if (ldap_source->cancellable) { + g_cancellable_release_fd (ldap_source->cancellable); + g_object_unref (ldap_source->cancellable); + } +} + +static GSourceFuncs socket_source_funcs = { + ldap_source_prepare, + ldap_source_check, + ldap_source_dispatch, + ldap_source_finalize, +}; + +GSource * +realm_ldap_connect_anonymous (GSocketAddress *address, + GSocketProtocol protocol, + GCancellable *cancellable) +{ + GSource *source; + LdapSource *ldap_source; + gchar *addrname; + GInetSocketAddress *inet; + int version; + gint port; + gchar *url; + LDAP *ldap; + int rc; + + g_return_val_if_fail (G_IS_INET_SOCKET_ADDRESS (address), NULL); + + inet = G_INET_SOCKET_ADDRESS (address); + addrname = g_inet_address_to_string (g_inet_socket_address_get_address (inet)); + port = g_inet_socket_address_get_port (inet); + if (port == 0) + port = 389; + + switch (protocol) { + case G_SOCKET_PROTOCOL_TCP: + url = g_strdup_printf ("ldap://%s:%d", addrname, port); + break; + case G_SOCKET_PROTOCOL_UDP: + url = g_strdup_printf ("cldap://%s:%d", addrname, port); + break; + default: + g_return_val_if_reached (NULL); + break; + } + + g_free (addrname); + + rc = ldap_initialize (&ldap, url); + g_free (url); + + /* Not an expected failure */ + if (rc != LDAP_SUCCESS) { + g_warning ("ldap_initialize() failed: %s", ldap_err2string (rc)); + return NULL; + } + + version = LDAP_VERSION3; + if (ldap_set_option (ldap, LDAP_OPT_PROTOCOL_VERSION, &version) != 0) + g_return_val_if_reached (NULL); + ldap_set_option (ldap, LDAP_OPT_REFERRALS , LDAP_OPT_OFF); + if (ldap_set_option (ldap, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_OFF) != 0) + g_warning ("openldap library does not support async connections"); + + source = g_source_new (&socket_source_funcs, sizeof (LdapSource)); + g_source_set_name (source, "LdapSource"); + ldap_source = (LdapSource *)source; + + ldap_source->ldap = ldap; + ldap_source->condition = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR; + + if (g_cancellable_make_pollfd (cancellable, + &ldap_source->cancel_pollfd)) { + ldap_source->cancellable = g_object_ref (cancellable); + g_source_add_poll (source, &ldap_source->cancel_pollfd); + } + + return source; +} + +void +realm_ldap_set_condition (GSource *source, + GIOCondition cond) +{ + LdapSource *ls = (LdapSource *)source; + GMainContext *context; + + ls->condition = cond | G_IO_HUP | G_IO_ERR; + + context = g_source_get_context (source); + if (context != NULL) + g_main_context_wakeup (context); +} + +void +realm_ldap_set_error (GError **error, + LDAP *ldap, + int code) +{ + char *info = NULL; + + if (code <= 0) { + if (ldap_get_option (ldap, LDAP_OPT_RESULT_CODE, &code) != 0) + g_return_if_reached (); + } + + if (code == LDAP_CANCELLED) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, + _("The operation was cancelled")); + return; + } + + if (ldap != NULL) { + if (ldap_get_option (ldap, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&info) != 0) + info = NULL; + } + + g_set_error_literal (error, REALM_LDAP_ERROR, code, + info ? info : ldap_err2string (code)); +} diff --git a/service/realm-ldap.h b/service/realm-ldap.h new file mode 100644 index 0000000..263f72a --- /dev/null +++ b/service/realm-ldap.h @@ -0,0 +1,45 @@ +/* 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_LDAP_H__ +#define __REALM_LDAP_H__ + +#include + +#include + +#define REALM_IO_CANCELLED 1 << 10 + +#define REALM_LDAP_ERROR (realm_ldap_error_get_quark ()) + +GQuark realm_ldap_error_get_quark (void) G_GNUC_CONST; + +void realm_ldap_set_error (GError **error, + LDAP *ldap, + int code); + +typedef GIOCondition (* RealmLdapCallback) (LDAP *ldap, + GIOCondition cond, + gpointer data); + +GSource * realm_ldap_connect_anonymous (GSocketAddress *address, + GSocketProtocol protocol, + GCancellable *cancellable); + +void realm_ldap_set_condition (GSource *source, + GIOCondition cond); + +#endif /* __REALM_LDAP_H__ */ diff --git a/service/realm-options.c b/service/realm-options.c index c576dae..fc28a3a 100644 --- a/service/realm-options.c +++ b/service/realm-options.c @@ -78,7 +78,7 @@ realm_options_computer_ou (GVariant *options, computer_ou = NULL; } - if (!computer_ou) { + if (realm_name && !computer_ou) { section = g_utf8_casefold (realm_name, -1); computer_ou = realm_settings_value (section, REALM_DBUS_OPTION_COMPUTER_OU); g_free (section); diff --git a/service/realm-provider.c b/service/realm-provider.c index a3040e4..e83e7bf 100644 --- a/service/realm-provider.c +++ b/service/realm-provider.c @@ -18,7 +18,6 @@ #include "realm-dbus-constants.h" #include "realm-dbus-generated.h" #include "realm-diagnostics.h" -#include "realm-discovery.h" #include "realm-errors.h" #include "realm-invocation.h" #include "realm-kerberos.h" @@ -221,7 +220,7 @@ RealmKerberos * realm_provider_lookup_or_register_realm (RealmProvider *self, GType realm_type, const gchar *realm_name, - GHashTable *discovery) + RealmDisco *disco) { RealmKerberos *realm; static gint unique_number = 0; @@ -231,8 +230,8 @@ realm_provider_lookup_or_register_realm (RealmProvider *self, realm = g_hash_table_lookup (self->pv->realms, realm_name); if (realm != NULL) { - if (discovery != NULL) - realm_kerberos_set_discovery (realm, discovery); + if (disco != NULL) + realm_kerberos_set_disco (realm, disco); return realm; } @@ -245,7 +244,7 @@ realm_provider_lookup_or_register_realm (RealmProvider *self, realm = g_object_new (realm_type, "name", realm_name, - "discovery", discovery, + "disco", disco, "provider", self, "g-object-path", path, NULL); diff --git a/service/realm-provider.h b/service/realm-provider.h index 6ceb848..e3be0bc 100644 --- a/service/realm-provider.h +++ b/service/realm-provider.h @@ -20,6 +20,7 @@ #include #include "realm-dbus-generated.h" +#include "realm-disco.h" #include "realm-kerberos.h" G_BEGIN_DECLS @@ -66,7 +67,7 @@ void realm_provider_stop_all (void); RealmKerberos * realm_provider_lookup_or_register_realm (RealmProvider *self, GType realm_type, const gchar *realm_name, - GHashTable *discovery); + RealmDisco *disco); gboolean realm_provider_is_default (const gchar *type, const gchar *name); diff --git a/service/realm-samba-enroll.c b/service/realm-samba-enroll.c index 3e46402..c6c48dc 100644 --- a/service/realm-samba-enroll.c +++ b/service/realm-samba-enroll.c @@ -19,9 +19,7 @@ #include "realm-daemon.h" #include "realm-dbus-constants.h" #include "realm-diagnostics.h" -#include "realm-discovery.h" #include "realm-errors.h" -#include "realm-kerberos-discover.h" #include "realm-options.h" #include "realm-samba-config.h" #include "realm-samba-enroll.h" @@ -71,12 +69,26 @@ join_closure_free (gpointer data) g_slice_free (JoinClosure, join); } +static gchar * +fallback_workgroup (const gchar *realm) +{ + const gchar *pos; + + pos = strchr (realm, '.'); + if (pos == NULL) + return g_utf8_strup (realm, -1); + else + return g_utf8_strup (realm, pos - realm); +} + static JoinClosure * join_closure_init (EggTask *task, const gchar *realm, + RealmDisco *disco, GDBusMethodInvocation *invocation) { JoinClosure *join; + gchar *workgroup; GError *error = NULL; int temp_fd; @@ -89,9 +101,29 @@ join_closure_init (EggTask *task, realm_ini_config_set (join->config, REALM_SAMBA_CONFIG_GLOBAL, "security", "ads", "kerberos method", "system keytab", - "realm", join->realm, + "realm", disco && disco->kerberos_realm ? disco->kerberos_realm : join->realm, NULL); + /* + * Samba complains if we don't set a 'workgroup' setting for the realm we're + * going to join. If we didn't yet manage to lookup the workgroup, then go ahead + * and assume that the first domain component is the workgroup name. + */ + + if (disco && disco->workgroup) { + realm_ini_config_set (join->config, REALM_SAMBA_CONFIG_GLOBAL, + "workgroup", disco->workgroup, NULL); + + } else { + workgroup = fallback_workgroup (realm); + realm_ini_config_set (join->config, REALM_SAMBA_CONFIG_GLOBAL, + "workgroup", workgroup, NULL); + if (disco) + disco->workgroup = workgroup; + else + g_free (workgroup); + } + /* Write out the config file for use by various net commands */ join->custom_smb_conf = g_build_filename (g_get_tmp_dir (), "realmd-smb-conf.XXXXXX", NULL); temp_fd = g_mkstemp_full (join->custom_smb_conf, O_WRONLY, S_IRUSR | S_IWUSR); @@ -244,178 +276,11 @@ on_join_do_keytab (GObject *source, g_object_unref (task); } -static gchar * -fallback_workgroup (const gchar *realm) -{ - const gchar *pos; - - pos = strchr (realm, '.'); - if (pos == NULL) - return g_utf8_strup (realm, -1); - else - return g_utf8_strup (realm, pos - realm); -} - -static void -begin_config_and_join (JoinClosure *join, - EggTask *task) -{ - GError *error = NULL; - gchar *workgroup; - - /* - * Samba complains if we don't set a 'workgroup' setting for the realm we're - * going to join. If we didn't yet manage to lookup the workgroup, then go ahead - * and assume that the first domain component is the workgroup name. - */ - workgroup = realm_ini_config_get (join->config, REALM_SAMBA_CONFIG_GLOBAL, "workgroup"); - if (workgroup == NULL) { - workgroup = fallback_workgroup (join->realm); - realm_diagnostics_info (join->invocation, "Calculated workgroup name: %s", workgroup); - realm_ini_config_set (join->config, REALM_SAMBA_CONFIG_GLOBAL, - "workgroup", workgroup, NULL); - } - free (workgroup); - - /* Write out the config file for various changes */ - realm_ini_config_write_file (join->config, NULL, &error); - - if (error != NULL) { - egg_task_return_error (task, error); - - /* Do join with a user name */ - } else if (join->user_name) { - begin_net_process (join, join->password_input, - on_join_do_keytab, g_object_ref (task), - "-U", join->user_name, "ads", "join", join->realm, - join->join_args[0], join->join_args[1], - join->join_args[2], join->join_args[3], - join->join_args[4], NULL); - - /* Do join with a ccache */ - } else { - begin_net_process (join, NULL, - on_join_do_keytab, g_object_ref (task), - "-k", "ads", "join", join->realm, - join->join_args[0], join->join_args[1], - join->join_args[2], join->join_args[3], - join->join_args[4], NULL); - } - -} - -static gchar * -find_workgroup_in_output (GString *output) -{ - const gchar *match = ":"; - const gchar *pos; - const gchar *end; - gchar *workgroup; - - /* Beginning */ - pos = g_strstr_len (output->str, output->len, match); - if (pos == NULL) - return NULL; - pos += strlen (match); - - /* Find the end */ - end = strchr (pos, '\n'); - if (end == NULL) - end = output->str + output->len; - - workgroup = g_strndup (pos, end - pos); - g_strstrip (workgroup); - return workgroup; -} - -static void -on_net_ads_workgroup (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - EggTask *task = EGG_TASK (user_data); - JoinClosure *join = egg_task_get_task_data (task); - GError *error = NULL; - GString *output = NULL; - gchar *workgroup; - gint status; - - status = realm_command_run_finish (result, &output, &error); - if (error == NULL && status != 0) { - g_set_error (&error, REALM_ERROR, REALM_ERROR_INTERNAL, - "Couldn't lookup domain info"); - } - - if (error == NULL) { - workgroup = find_workgroup_in_output (output); - if (workgroup) { - realm_diagnostics_info (join->invocation, "Looked up workgroup name: %s", workgroup); - realm_ini_config_set (join->config, REALM_SAMBA_CONFIG_GLOBAL, - "workgroup", workgroup, NULL); - g_free (workgroup); - } - - g_string_free (output, TRUE); - - } else { - realm_diagnostics_error (join->invocation, error, NULL); - g_error_free (error); - } - - begin_config_and_join (join, task); - - g_object_unref (task); -} - - -static void -begin_net_lookup (JoinClosure *join, - EggTask *task, - GHashTable *discovery) -{ - const gchar **kdcs; - - kdcs = realm_discovery_get_strings (discovery, REALM_DBUS_DISCOVERY_KDCS); - - /* If we discovered KDCs then try to ask first one what the workgroup name is */ - if (kdcs && kdcs[0]) { - begin_net_process (join, NULL, - on_net_ads_workgroup, g_object_ref (task), - "ads", "workgroup", "-S", kdcs[0], NULL); - - } else { - begin_config_and_join (join, task); - } -} - -static void -on_discover_do_lookup (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - EggTask *task = EGG_TASK (user_data); - JoinClosure *join = egg_task_get_task_data (task); - GError *error = NULL; - GHashTable *discovery; - - realm_kerberos_discover_finish (result, &discovery, &error); - if (error == NULL) { - begin_net_lookup (join, task, discovery); - g_hash_table_unref (discovery); - - } else { - egg_task_return_error (task, error); - } - - g_object_unref (task); -} - static void begin_join (EggTask *task, JoinClosure *join, const gchar *realm, - GVariant *options, - GHashTable *discovery) + GVariant *options) { const gchar *computer_ou; gchar *strange_ou; @@ -459,12 +324,23 @@ begin_join (EggTask *task, if (error != NULL) { egg_task_return_error (task, error); - } else if (discovery) { - begin_net_lookup (join, task, discovery); + /* Do join with a user name */ + } else if (join->user_name) { + begin_net_process (join, join->password_input, + on_join_do_keytab, g_object_ref (task), + "-U", join->user_name, "ads", "join", join->realm, + join->join_args[0], join->join_args[1], + join->join_args[2], join->join_args[3], + join->join_args[4], NULL); + /* Do join with a ccache */ } else { - realm_kerberos_discover_async (join->realm, join->invocation, - on_discover_do_lookup, g_object_ref (task)); + begin_net_process (join, NULL, + on_join_do_keytab, g_object_ref (task), + "-k", "ads", "join", join->realm, + join->join_args[0], join->join_args[1], + join->join_args[2], join->join_args[3], + join->join_args[4], NULL); } } @@ -472,7 +348,7 @@ void realm_samba_enroll_join_async (const gchar *realm, RealmCredential *cred, GVariant *options, - GHashTable *discovery, + RealmDisco *disco, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, gpointer user_data) @@ -484,7 +360,7 @@ realm_samba_enroll_join_async (const gchar *realm, g_return_if_fail (cred != NULL); task = egg_task_new (NULL, NULL, callback, user_data); - join = join_closure_init (task, realm, invocation); + join = join_closure_init (task, realm, disco, invocation); switch (cred->type) { case REALM_CREDENTIAL_PASSWORD: @@ -498,7 +374,7 @@ realm_samba_enroll_join_async (const gchar *realm, g_return_if_reached (); } - begin_join (task, join, realm, options, discovery); + begin_join (task, join, realm, options); g_object_unref (task); } @@ -557,7 +433,7 @@ realm_samba_enroll_leave_async (const gchar *realm, JoinClosure *join; task = egg_task_new (NULL, NULL, callback, user_data); - join = join_closure_init (task, realm, invocation); + join = join_closure_init (task, realm, NULL, invocation); switch (cred->type) { case REALM_CREDENTIAL_PASSWORD: diff --git a/service/realm-samba-enroll.h b/service/realm-samba-enroll.h index 8fc5cbe..163c8dc 100644 --- a/service/realm-samba-enroll.h +++ b/service/realm-samba-enroll.h @@ -18,6 +18,7 @@ #define __REALM_SAMBA_ENROLL_H__ #include "realm-credential.h" +#include "realm-disco.h" #include @@ -28,7 +29,7 @@ G_BEGIN_DECLS void realm_samba_enroll_join_async (const gchar *realm, RealmCredential *cred, GVariant *options, - GHashTable *discovery, + RealmDisco *disco, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, gpointer user_data); diff --git a/service/realm-samba-provider.c b/service/realm-samba-provider.c index c564479..c3a12c7 100644 --- a/service/realm-samba-provider.c +++ b/service/realm-samba-provider.c @@ -19,10 +19,9 @@ #include "realm-daemon.h" #include "realm-dbus-constants.h" #include "realm-diagnostics.h" -#include "realm-discovery.h" +#include "realm-disco-domain.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" @@ -93,7 +92,14 @@ on_ad_discover (GObject *source, gpointer user_data) { EggTask *task = EGG_TASK (user_data); - egg_task_return_pointer (task, g_object_ref (result), g_object_unref); + RealmDisco *disco; + GError *error = NULL; + + disco = realm_disco_domain_finish (result, &error); + if (error) + egg_task_return_error (task, error); + else + egg_task_return_pointer (task, disco, realm_disco_unref); g_object_unref (task); } @@ -116,8 +122,8 @@ realm_samba_provider_discover_async (RealmProvider *provider, egg_task_return_pointer (task, NULL, NULL); } else { - realm_kerberos_discover_async (string, invocation, on_ad_discover, - g_object_ref (task)); + realm_disco_domain_async (string, invocation, + on_ad_discover, g_object_ref (task)); } g_object_unref (task); @@ -130,29 +136,19 @@ realm_samba_provider_discover_finish (RealmProvider *provider, GError **error) { RealmKerberos *realm = NULL; - GHashTable *discovery; - GAsyncResult *ad_result; - gchar *name; - - ad_result = egg_task_propagate_pointer (EGG_TASK (result), error); - if (ad_result == NULL) - return NULL; + RealmDisco *disco; - name = realm_kerberos_discover_finish (ad_result, &discovery, error); - if (name == NULL) + disco = egg_task_propagate_pointer (EGG_TASK (result), error); + if (disco == NULL) return NULL; - if (realm_discovery_has_string (discovery, - REALM_DBUS_OPTION_SERVER_SOFTWARE, - REALM_DBUS_IDENTIFIER_ACTIVE_DIRECTORY)) { - + if (g_strcmp0 (disco->server_software, REALM_DBUS_IDENTIFIER_ACTIVE_DIRECTORY) == 0) { realm = realm_provider_lookup_or_register_realm (provider, REALM_TYPE_SAMBA, - name, discovery); + disco->domain_name, disco); } - g_free (name); - g_hash_table_unref (discovery); + realm_disco_unref (disco); if (realm == NULL) return NULL; diff --git a/service/realm-samba.c b/service/realm-samba.c index b7c019c..58a098b 100644 --- a/service/realm-samba.c +++ b/service/realm-samba.c @@ -19,10 +19,9 @@ #include "realm-daemon.h" #include "realm-dbus-constants.h" #include "realm-diagnostics.h" -#include "realm-discovery.h" +#include "realm-disco.h" #include "realm-errors.h" #include "realm-kerberos.h" -#include "realm-kerberos-discover.h" #include "realm-kerberos-membership.h" #include "realm-options.h" #include "realm-packages.h" @@ -233,7 +232,7 @@ on_install_do_join (GObject *source, realm_packages_install_finish (result, &error); if (error == NULL) { realm_samba_enroll_join_async (enroll->realm_name, enroll->cred, - enroll->options, realm_kerberos_get_discovery (kerberos), + enroll->options, realm_kerberos_get_disco (kerberos), enroll->invocation, on_join_do_winbind, g_object_ref (task)); } else { diff --git a/service/realm-sssd-ad.c b/service/realm-sssd-ad.c index 290137d..b0a9ab4 100644 --- a/service/realm-sssd-ad.c +++ b/service/realm-sssd-ad.c @@ -296,7 +296,7 @@ on_install_do_join (GObject *source, realm_samba_enroll_join_async (join->realm_name, join->cred, join->options, - realm_kerberos_get_discovery (kerberos), + realm_kerberos_get_disco (kerberos), join->invocation, on_join_do_sssd, g_object_ref (task)); } diff --git a/service/realm-sssd-ipa.c b/service/realm-sssd-ipa.c index ae803d0..aa7f937 100644 --- a/service/realm-sssd-ipa.c +++ b/service/realm-sssd-ipa.c @@ -18,9 +18,7 @@ #include "realm-daemon.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.h" #include "realm-kerberos-membership.h" #include "realm-options.h" diff --git a/service/realm-sssd-provider.c b/service/realm-sssd-provider.c index 5de7742..454847b 100644 --- a/service/realm-sssd-provider.c +++ b/service/realm-sssd-provider.c @@ -19,10 +19,9 @@ #include "realm-daemon.h" #include "realm-dbus-constants.h" #include "realm-diagnostics.h" -#include "realm-discovery.h" +#include "realm-disco-domain.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-ipa.h" @@ -99,7 +98,14 @@ on_kerberos_discover (GObject *source, gpointer user_data) { EggTask *task = EGG_TASK (user_data); - egg_task_return_pointer (task, g_object_ref (result), g_object_unref); + RealmDisco *disco; + GError *error = NULL; + + disco = realm_disco_domain_finish (result, &error); + if (error) + egg_task_return_error (task, error); + else + egg_task_return_pointer (task, disco, realm_disco_unref); g_object_unref (task); } @@ -130,8 +136,8 @@ realm_sssd_provider_discover_async (RealmProvider *provider, egg_task_return_pointer (task, NULL, NULL); } else { - realm_kerberos_discover_async (string, invocation, on_kerberos_discover, - g_object_ref (task)); + realm_disco_domain_async (string, invocation, on_kerberos_discover, + g_object_ref (task)); } g_object_unref (task); @@ -143,41 +149,28 @@ realm_sssd_provider_discover_finish (RealmProvider *provider, gint *relevance, GError **error) { - GAsyncResult *ad_result; RealmKerberos *realm = NULL; - GHashTable *discovery; + RealmDisco *disco; gint priority; - gchar *name; - - ad_result = egg_task_propagate_pointer (EGG_TASK (result), error); - if (ad_result == NULL) - return NULL; - name = realm_kerberos_discover_finish (ad_result, &discovery, error); - if (name == NULL) + disco = egg_task_propagate_pointer (EGG_TASK (result), error); + if (disco == NULL) return NULL; - if (realm_discovery_has_string (discovery, - REALM_DBUS_OPTION_SERVER_SOFTWARE, - REALM_DBUS_IDENTIFIER_ACTIVE_DIRECTORY)) { - + if (g_strcmp0 (disco->server_software, REALM_DBUS_IDENTIFIER_ACTIVE_DIRECTORY) == 0) { realm = realm_provider_lookup_or_register_realm (provider, REALM_TYPE_SSSD_AD, - name, discovery); + disco->domain_name, disco); priority = realm_provider_is_default (REALM_DBUS_IDENTIFIER_ACTIVE_DIRECTORY, REALM_DBUS_IDENTIFIER_SSSD) ? 100 : 50; - } else if (realm_discovery_has_string (discovery, - REALM_DBUS_OPTION_SERVER_SOFTWARE, - REALM_DBUS_IDENTIFIER_FREEIPA)) { - + } else if (g_strcmp0 (disco->server_software, REALM_DBUS_IDENTIFIER_FREEIPA) == 0) { realm = realm_provider_lookup_or_register_realm (provider, REALM_TYPE_SSSD_IPA, - name, discovery); + disco->domain_name, disco); priority = 100; } - g_free (name); - g_hash_table_unref (discovery); + realm_disco_unref (disco); if (realm == NULL) return NULL; diff --git a/service/realm-sssd.c b/service/realm-sssd.c index 95f99da..c4e39c5 100644 --- a/service/realm-sssd.c +++ b/service/realm-sssd.c @@ -19,7 +19,6 @@ #include "realm-daemon.h" #include "realm-dbus-constants.h" #include "realm-diagnostics.h" -#include "realm-discovery.h" #include "realm-errors.h" #include "realm-packages.h" #include "realm-provider.h" @@ -232,11 +231,13 @@ update_realm_name (RealmSssd *self) { RealmKerberos *kerberos = REALM_KERBEROS (self); const char *name; + RealmDisco *disco; gchar *realm = NULL; if (self->pv->section == NULL) { - realm = g_strdup (realm_discovery_get_string (realm_kerberos_get_discovery (kerberos), - REALM_DBUS_DISCOVERY_REALM)); + disco = realm_kerberos_get_disco (kerberos); + if (disco != NULL) + realm = g_strdup (disco->kerberos_realm); } else { realm = realm_ini_config_get (self->pv->config, self->pv->section, "krb5_realm"); } @@ -255,11 +256,13 @@ update_domain (RealmSssd *self) { RealmKerberos *kerberos = REALM_KERBEROS (self); const char *name; + RealmDisco *disco; gchar *domain = NULL; if (self->pv->section == NULL) { - domain = g_strdup (realm_discovery_get_string (realm_kerberos_get_discovery (kerberos), - REALM_DBUS_DISCOVERY_DOMAIN)); + disco = realm_kerberos_get_disco (kerberos); + if (disco != NULL) + domain = g_strdup (disco->domain_name); } else { domain = realm_ini_config_get (self->pv->config, self->pv->section, "dns_discovery_domain"); } -- 1.8.1.4