From eada6174bc1f12f7159d01cfacc9edfae4c6455e Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Tue, 4 Sep 2012 10:22:03 +0200 Subject: [PATCH] WIP IPA client https://bugs.freedesktop.org/show_bug.cgi?id=55041 --- service/realm-command.c | 37 +++++ service/realm-command.h | 2 + service/realm-samba-enroll.c | 36 +---- service/realm-sssd-ipa.c | 317 +++++++++++++++++++++++++++++++++++++++++- service/realmd-defaults.conf | 1 + 5 files changed, 358 insertions(+), 35 deletions(-) diff --git a/service/realm-command.c b/service/realm-command.c index dafd6ed..0d28046 100644 --- a/service/realm-command.c +++ b/service/realm-command.c @@ -626,3 +626,40 @@ realm_command_run_finish (GAsyncResult *result, return command->exit_code; } + +static void +clear_and_free_password (gpointer data) +{ + gchar *password = data; + memset ((char *)password, 0, strlen (password)); + g_free (password); +} + +GBytes * +realm_command_build_password_line (GBytes *password) +{ + GByteArray *array; + gconstpointer data; + gsize length; + guchar *result; + + array = g_byte_array_new (); + data = g_bytes_get_data (password, &length); + g_byte_array_append (array, data, length); + + /* + * We add a new line, which getpass() used inside command expects + */ + g_byte_array_append (array, (guchar *)"\n", 1); + length = array->len; + + /* + * In addition we add null terminator. This is not + * written to 'net' command, but used by clear_and_free_password(). + */ + g_byte_array_append (array, (guchar *)"\0", 1); + + result = g_byte_array_free (array, FALSE); + return g_bytes_new_with_free_func (result, length, + clear_and_free_password, result); +} diff --git a/service/realm-command.h b/service/realm-command.h index 69b977c..f13ebc1 100644 --- a/service/realm-command.h +++ b/service/realm-command.h @@ -38,6 +38,8 @@ gint realm_command_run_finish (GAsyncResult *re GString **output, GError **error); +GBytes * realm_command_build_password_line (GBytes *password); + G_END_DECLS #endif /* REALM_COMMAND_H */ diff --git a/service/realm-samba-enroll.c b/service/realm-samba-enroll.c index 29e618a..f487305 100644 --- a/service/realm-samba-enroll.c +++ b/service/realm-samba-enroll.c @@ -43,14 +43,6 @@ typedef struct { } JoinClosure; static void -clear_and_free_password (gpointer data) -{ - gchar *password = data; - memset ((char *)password, 0, strlen (password)); - g_free (password); -} - -static void join_closure_free (gpointer data) { JoinClosure *join = data; @@ -75,37 +67,13 @@ join_closure_init (const gchar *realm, GDBusMethodInvocation *invocation) { JoinClosure *join; - GByteArray *array; - const guchar *data; - guchar *input; - gsize length; join = g_slice_new0 (JoinClosure); join->realm = g_strdup (realm); join->invocation = invocation ? g_object_ref (invocation) : NULL; - if (password) { - array = g_byte_array_new (); - data = g_bytes_get_data (password, &length); - g_byte_array_append (array, data, length); - - /* - * We add a new line, which getpass() used inside net - * command expects - */ - g_byte_array_append (array, (guchar *)"\n", 1); - length = array->len; - - /* - * In addition we add null terminator. This is not - * written to 'net' command, but used by clear_and_free_password(). - */ - g_byte_array_append (array, (guchar *)"\0", 1); - - input = g_byte_array_free (array, FALSE); - join->password_input = g_bytes_new_with_free_func (input, length, - clear_and_free_password, input); - } + if (password) + join->password_input = realm_command_build_password_line (password); join->user_name = g_strdup (user_name); diff --git a/service/realm-sssd-ipa.c b/service/realm-sssd-ipa.c index ed9dee8..be002fe 100644 --- a/service/realm-sssd-ipa.c +++ b/service/realm-sssd-ipa.c @@ -21,13 +21,17 @@ #include "realm-diagnostics.h" #include "realm-discovery.h" #include "realm-errors.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,11 @@ typedef struct { RealmSssdClass parent_class; } RealmSssdIpaClass; -G_DEFINE_TYPE (RealmSssdIpa, realm_sssd_ipa, REALM_TYPE_SSSD); +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 +61,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 +69,25 @@ 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"); } void @@ -68,3 +96,290 @@ 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; +} EnrollClosure; + +static void +enroll_closure_free (gpointer data) +{ + EnrollClosure *enroll = data; + g_object_unref (enroll->invocation); + 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; + 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_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 +enroll_ipa_client_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; + gchar **environ; + + 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->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 { + argv[0] = realm_settings_string ("paths", "ipa-client-install"); + environ = g_environ_setenv (g_get_environ (), "LC_ALL", "C", TRUE); + + realm_command_runv_async ((gchar **)argv, environ, input, invocation, NULL, + on_ipa_client_do_restart, g_object_ref (async)); + + g_strfreev (environ); + } + + 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[] = { + "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); + + enroll_ipa_client_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[] = { + "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); + + enroll_ipa_client_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 +realm_sssd_ipa_unenroll_async (RealmKerberosMembership *membership, + const char *name, + GBytes *password, + 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; + GBytes *input; + gchar **environ; + + 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 { + const gchar *argv[] = { + "ipa-client-install", + "--uninstall", "--unattended", + NULL + }; + + argv[0] = realm_settings_string ("commands", "ipa-client-install"); + environ = g_environ_setenv (g_get_environ (), "LC_ALL", "C", TRUE); + + input = realm_command_build_password_line (password); + realm_command_runv_async ((gchar **)argv, environ, NULL, invocation, NULL, + on_ipa_client_do_disable, g_object_ref (async)); + + g_bytes_unref (input); + g_strfreev (environ); + } + + g_object_unref (async); +} + +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_unenroll_async; + iface->unenroll_finish = realm_sssd_ipa_generic_finish; +} diff --git a/service/realmd-defaults.conf b/service/realmd-defaults.conf index 717fbbd..cf10360 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 -- 1.7.10.4