From b1c142f311576dedd519d559859c2b72d1200e46 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Thu, 22 Nov 2012 17:47:49 +0100 Subject: [PATCH] WIP IPA client https://bugs.freedesktop.org/show_bug.cgi?id=55041 --- service/realm-sssd-ipa.c | 403 ++++++++++++++++++++++++++++++++++++++++++- service/realmd-defaults.conf | 1 + service/realmd-redhat.conf | 3 + 3 files changed, 405 insertions(+), 2 deletions(-) diff --git a/service/realm-sssd-ipa.c b/service/realm-sssd-ipa.c index ed9dee8..a97bd46 100644 --- a/service/realm-sssd-ipa.c +++ b/service/realm-sssd-ipa.c @@ -14,20 +14,24 @@ #include "config.h" -#include "realm-ipa-discover.h" #include "realm-command.h" #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-packages.h" #include "realm-provider.h" #include "realm-service.h" +#include "realm-settings.h" #include "realm-sssd.h" #include "realm-sssd-ipa.h" #include "realm-sssd-config.h" +#include #include #include @@ -41,7 +45,17 @@ typedef struct { RealmSssdClass parent_class; } RealmSssdIpaClass; -G_DEFINE_TYPE (RealmSssdIpa, realm_sssd_ipa, REALM_TYPE_SSSD); +static const gchar *IPA_PACKAGES[] = { + REALM_DBUS_IDENTIFIER_FREEIPA, + REALM_DBUS_IDENTIFIER_SSSD, + NULL +}; + +static void realm_sssd_ipa_kerberos_membership_iface (RealmKerberosMembershipIface *iface); + +G_DEFINE_TYPE_WITH_CODE (RealmSssdIpa, realm_sssd_ipa, REALM_TYPE_SSSD, + G_IMPLEMENT_INTERFACE (REALM_TYPE_KERBEROS_MEMBERSHIP, realm_sssd_ipa_kerberos_membership_iface); +); static void realm_sssd_ipa_init (RealmSssdIpa *self) @@ -53,6 +67,7 @@ static void realm_sssd_ipa_constructed (GObject *obj) { RealmKerberos *kerberos = REALM_KERBEROS (obj); + GVariant *supported; G_OBJECT_CLASS (realm_sssd_ipa_parent_class)->constructed (obj); @@ -60,6 +75,26 @@ realm_sssd_ipa_constructed (GObject *obj) REALM_DBUS_OPTION_SERVER_SOFTWARE, REALM_DBUS_IDENTIFIER_FREEIPA, REALM_DBUS_OPTION_CLIENT_SOFTWARE, REALM_DBUS_IDENTIFIER_SSSD, NULL); + + /* + * NOTE: The ipa-client-install service requires that we pass a password directly + * to the process, and not a ccache. It also accepts a one time password. + */ + supported = realm_kerberos_membership_build_supported ( + REALM_KERBEROS_CREDENTIAL_PASSWORD, REALM_KERBEROS_OWNER_ADMIN, + REALM_KERBEROS_CREDENTIAL_SECRET, REALM_KERBEROS_OWNER_NONE, + 0); + + realm_kerberos_set_supported_join_creds (kerberos, supported); + + supported = realm_kerberos_membership_build_supported ( + REALM_KERBEROS_CREDENTIAL_PASSWORD, REALM_KERBEROS_OWNER_ADMIN, + 0); + + realm_kerberos_set_supported_leave_creds (kerberos, supported); + + realm_kerberos_set_suggested_admin (kerberos, "admin"); + realm_kerberos_set_required_package_sets (kerberos, IPA_PACKAGES); } void @@ -68,3 +103,367 @@ realm_sssd_ipa_class_init (RealmSssdIpaClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = realm_sssd_ipa_constructed; } + +typedef struct { + GDBusMethodInvocation *invocation; + gchar **argv; + GBytes *input; +} EnrollClosure; + +static void +enroll_closure_free (gpointer data) +{ + EnrollClosure *enroll = data; + g_object_unref (enroll->invocation); + g_strfreev (enroll->argv); + if (enroll->input) + g_bytes_unref (enroll->input); + g_slice_free (EnrollClosure, enroll); +} + +static void +on_restart_done (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data); + RealmSssd *sssd = REALM_SSSD (g_async_result_get_source_object (user_data)); + GError *error = NULL; + + realm_service_enable_and_restart_finish (result, &error); + if (error == NULL) + realm_sssd_update_properties (sssd); + else + g_simple_async_result_take_error (async, error); + g_simple_async_result_complete (async); + + g_object_unref (sssd); + g_object_unref (async); +} + +static void +on_ipa_client_do_restart (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data); + EnrollClosure *enroll = g_simple_async_result_get_op_res_gpointer (async); + GError *error = NULL; + GString *output = NULL; + gint status; + + status = realm_command_run_finish (result, &output, &error); + + if (error == NULL && status != 0) { + + /* + * TODO: We need to update ipa-client-install to accept a + * ccache so we can get better feedback on invalid passwords. + * We run the process with LC_ALL=C so at least we know these + * messages will be in english. + */ + if (g_pattern_match_simple ("*kinit: Password incorrect*", output->str)) { + g_set_error (&error, REALM_ERROR, REALM_ERROR_AUTH_FAILED, + "Password is incorrect"); + } else { + g_set_error (&error, REALM_ERROR, REALM_ERROR_INTERNAL, + "Running ipa-client-install failed"); + } + } + + if (error == NULL) { + realm_service_enable_and_restart ("sssd", enroll->invocation, + on_restart_done, g_object_ref (async)); + + } else { + g_simple_async_result_take_error (async, error); + g_simple_async_result_complete (async); + } + + g_object_unref (async); +} + +static void +on_install_do_join (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data); + EnrollClosure *enroll = g_simple_async_result_get_op_res_gpointer (async); + GError *error = NULL; + + const gchar *env[] = { + "LANG=C", + NULL, + }; + + realm_packages_install_finish (result, &error); + if (error == NULL) { + realm_command_runv_async (enroll->argv, (gchar **)env, enroll->input, + enroll->invocation, NULL, + on_ipa_client_do_restart, g_object_ref (async)); + } else { + g_simple_async_result_take_error (async, error); + g_simple_async_result_complete (async); + } + + g_object_unref (async); +} + +static void +join_ipa_async (RealmKerberosMembership *membership, + const gchar **argv, + GBytes *input, + RealmKerberosFlags flags, + GVariant *options, + GDBusMethodInvocation *invocation, + GAsyncReadyCallback callback, + gpointer user_data) +{ + RealmKerberos *realm = REALM_KERBEROS (membership); + RealmSssd *sssd = REALM_SSSD (realm); + GSimpleAsyncResult *async; + EnrollClosure *enroll; + const gchar *domain_name; + const gchar *computer_ou; + + domain_name = realm_kerberos_get_name (realm); + + async = g_simple_async_result_new (G_OBJECT (realm), callback, user_data, NULL); + enroll = g_slice_new0 (EnrollClosure); + enroll->input = input ? g_bytes_ref (input) : NULL; + enroll->argv = g_strdupv ((gchar **)argv); + enroll->invocation = g_object_ref (invocation); + g_simple_async_result_set_op_res_gpointer (async, enroll, enroll_closure_free); + + if (g_variant_lookup (options, REALM_DBUS_OPTION_COMPUTER_OU, "&s", &computer_ou)) { + g_simple_async_result_set_error (async, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + _("The computer-ou argument is not supported when joining an IPA domain.")); + g_simple_async_result_complete_in_idle (async); + + } else if (realm_sssd_get_config_section (sssd) != NULL) { + g_simple_async_result_set_error (async, REALM_ERROR, REALM_ERROR_ALREADY_CONFIGURED, + _("Already joined to this domain")); + g_simple_async_result_complete_in_idle (async); + + } else if (realm_sssd_config_have_domain (realm_sssd_get_config (sssd), domain_name)) { + g_simple_async_result_set_error (async, REALM_ERROR, REALM_ERROR_ALREADY_CONFIGURED, + _("A domain with this name is already configured")); + g_simple_async_result_complete_in_idle (async); + + } else { + realm_packages_install_async (IPA_PACKAGES, invocation, + on_install_do_join, g_object_ref (async)); + } + + g_object_unref (async); +} + +static void +realm_sssd_ipa_enroll_password_async (RealmKerberosMembership *membership, + const gchar *name, + GBytes *password, + RealmKerberosFlags flags, + GVariant *options, + GDBusMethodInvocation *invocation, + GAsyncReadyCallback callback, + gpointer user_data) +{ + RealmKerberos *realm = REALM_KERBEROS (membership); + GBytes *input; + + const gchar *argv[] = { + realm_settings_string ("paths", "ipa-client-install"), + "--domain", realm_kerberos_get_name (realm), + "--realm", realm_kerberos_get_realm_name (realm), + "--principal", name, + "-W", + "--mkhomedir", + "--no-ntp", + "--enable-dns-updates", + "--permit", + "--unattended", + NULL, + }; + + input = realm_command_build_password_line (password); + + join_ipa_async (membership, argv, input, flags, options, + invocation, callback, user_data); + + g_bytes_unref (input); +} + +static void +realm_sssd_ipa_enroll_secret_async (RealmKerberosMembership *membership, + GBytes *secret, + RealmKerberosFlags flags, + GVariant *options, + GDBusMethodInvocation *invocation, + GAsyncReadyCallback callback, + gpointer user_data) +{ + RealmKerberos *realm = REALM_KERBEROS (membership); + GBytes *input; + + const gchar *argv[] = { + realm_settings_string ("paths", "ipa-client-install"), + "--domain", realm_kerberos_get_name (realm), + "--realm", realm_kerberos_get_realm_name (realm), + "-W", + "--mkhomedir", + "--no-ntp", + "--enable-dns-updates", + "--permit", + "--unattended", + NULL, + }; + + input = realm_command_build_password_line (secret); + + join_ipa_async (membership, argv, input, flags, options, + invocation, callback, user_data); + + g_bytes_unref (input); +} + +static void +on_ipa_client_do_disable (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data); + EnrollClosure *enroll = g_simple_async_result_get_op_res_gpointer (async); + RealmSssd *sssd = REALM_SSSD (g_async_result_get_source_object (user_data)); + GError *error = NULL; + gint status; + + status = realm_command_run_finish (result, NULL, &error); + + if (error == NULL && status != 0) { + g_set_error (&error, REALM_ERROR, REALM_ERROR_INTERNAL, + "Running ipa-client-install failed"); + } + + if (error == NULL) { + realm_sssd_deconfigure_domain_tail (sssd, async, enroll->invocation); + + } else { + g_simple_async_result_take_error (async, error); + g_simple_async_result_complete (async); + } + + g_object_unref (sssd); + g_object_unref (async); +} + +static void +leave_ipa_async (RealmKerberosMembership *membership, + const gchar **argv, + GBytes *input, + RealmKerberosFlags flags, + GVariant *options, + GDBusMethodInvocation *invocation, + GAsyncReadyCallback callback, + gpointer user_data) +{ + RealmKerberos *realm = REALM_KERBEROS (membership); + RealmSssd *sssd = REALM_SSSD (realm); + GSimpleAsyncResult *async; + EnrollClosure *enroll; + const gchar *computer_ou; + + const gchar *env[] = { + "LANG=C", + NULL, + }; + + async = g_simple_async_result_new (G_OBJECT (realm), callback, user_data, NULL); + enroll = g_slice_new0 (EnrollClosure); + enroll->invocation = g_object_ref (invocation); + g_simple_async_result_set_op_res_gpointer (async, enroll, enroll_closure_free); + + if (realm_sssd_get_config_section (sssd) == NULL) { + g_simple_async_result_set_error (async, REALM_ERROR, REALM_ERROR_NOT_CONFIGURED, + _("Not currently joined to this realm")); + g_simple_async_result_complete_in_idle (async); + + } else if (g_variant_lookup (options, REALM_DBUS_OPTION_COMPUTER_OU, "&s", &computer_ou)) { + g_simple_async_result_set_error (async, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, + "The computer-ou argument is not supported when leaving an IPA domain."); + g_simple_async_result_complete_in_idle (async); + + } else { + realm_command_runv_async ((gchar **)argv, (gchar **)env, NULL, invocation, NULL, + on_ipa_client_do_disable, g_object_ref (async)); + } + + g_object_unref (async); +} + +static void +realm_sssd_ipa_leave_password_async (RealmKerberosMembership *membership, + const char *name, + GBytes *password, + RealmKerberosFlags flags, + GVariant *options, + GDBusMethodInvocation *invocation, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GBytes *input; + + const gchar *argv[] = { + realm_settings_string ("paths", "ipa-client-install"), + "--uninstall", + "--principal", name, + "-W", + NULL + }; + + input = realm_command_build_password_line (password); + leave_ipa_async (membership, argv, input, flags, options, + invocation, callback, user_data); + g_bytes_unref (input); +} + +static void +realm_sssd_ipa_leave_automatic_async (RealmKerberosMembership *membership, + RealmKerberosFlags flags, + GVariant *options, + GDBusMethodInvocation *invocation, + GAsyncReadyCallback callback, + gpointer user_data) +{ + const gchar *argv[] = { + realm_settings_string ("paths", "ipa-client-install"), + "--uninstall", + "--unattended", + NULL + }; + + leave_ipa_async (membership, argv, NULL, flags, options, + invocation, callback, user_data); +} + +static gboolean +realm_sssd_ipa_generic_finish (RealmKerberosMembership *realm, + GAsyncResult *result, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + + return TRUE; +} + +static void +realm_sssd_ipa_kerberos_membership_iface (RealmKerberosMembershipIface *iface) +{ + iface->enroll_password_async = realm_sssd_ipa_enroll_password_async; + iface->enroll_secret_async = realm_sssd_ipa_enroll_secret_async; + iface->enroll_finish = realm_sssd_ipa_generic_finish; + iface->unenroll_password_async = realm_sssd_ipa_leave_password_async; + iface->unenroll_automatic_async = realm_sssd_ipa_leave_automatic_async; + iface->unenroll_finish = realm_sssd_ipa_generic_finish; +} diff --git a/service/realmd-defaults.conf b/service/realmd-defaults.conf index 6ad6a70..07fa133 100644 --- a/service/realmd-defaults.conf +++ b/service/realmd-defaults.conf @@ -6,6 +6,7 @@ winbindd = /usr/sbin/winbindd smb.conf = /etc/smb.conf sssd.conf = /etc/sssd/sssd.conf adcli = /usr/sbin/adcli +ipa-client-install = /usr/sbin/ipa-client-install [active-directory] default-client = sssd diff --git a/service/realmd-redhat.conf b/service/realmd-redhat.conf index 0fc0cd2..d64d08e 100644 --- a/service/realmd-redhat.conf +++ b/service/realmd-redhat.conf @@ -16,6 +16,9 @@ sssd-tools = /usr/sbin/sss_cache [adcli-packages] adcli = /usr/sbin/adcli +[freeipa-packages] +freeipa-client = /usr/sbin/ipa-client-install + [commands] winbind-enable-logins = /usr/sbin/authconfig --update --enablewinbind --enablewinbindauth --enablemkhomedir winbind-disable-logins = /usr/sbin/authconfig --update --disablewinbind --disablewinbindauth -- 1.8.0