From bfeb6590f1d2d3510dde646b306da6b2153c322a Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Tue, 26 Mar 2013 16:23:05 +0100 Subject: [PATCH] Cleanup our samba net ads join process * Don't write stuff out to the regitsry or depend on it working. * Use a custom smb.conf file for pre-join parameters. * Use 'net ads lookup' to discover the workgroup name which samba absolutely insists on having before the join starts >:( * Do another DNS discovery if there was no DNS discovery done before the join starts https://bugs.freedesktop.org/show_bug.cgi?id=56023 --- service/Makefile.am | 1 - service/net-ads-smb.conf | 2 - service/realm-discovery.c | 65 +++------- service/realm-discovery.h | 7 +- service/realm-ini-config.c | 49 ++++++++ service/realm-ini-config.h | 4 + service/realm-kerberos-discover.c | 15 ++- service/realm-samba-enroll.c | 258 ++++++++++++++++++++++++-------------- service/realm-samba-enroll.h | 1 + service/realm-samba.c | 7 +- service/realm-sssd-ad.c | 6 +- 11 files changed, 260 insertions(+), 155 deletions(-) delete mode 100644 service/net-ads-smb.conf diff --git a/service/Makefile.am b/service/Makefile.am index f5c1fe4..2f4bc12 100644 --- a/service/Makefile.am +++ b/service/Makefile.am @@ -10,7 +10,6 @@ REALMD_CONFIGS = \ $(NULL) private_DATA = \ - net-ads-smb.conf \ realmd-defaults.conf polkit_policydir = $(POLKIT_ACTION_DIR) diff --git a/service/net-ads-smb.conf b/service/net-ads-smb.conf deleted file mode 100644 index 0ebb082..0000000 --- a/service/net-ads-smb.conf +++ /dev/null @@ -1,2 +0,0 @@ -[global] - config backend = registry \ No newline at end of file diff --git a/service/realm-discovery.c b/service/realm-discovery.c index ffd6be7..f9400ad 100644 --- a/service/realm-discovery.c +++ b/service/realm-discovery.c @@ -78,56 +78,31 @@ realm_discovery_add_variant (GHashTable *discovery, } void -realm_discovery_add_srv_targets (GHashTable *discovery, - const gchar *type, - GList *targets) +realm_discovery_add_strings (GHashTable *discovery, + const gchar *type, + const char **value) { - GPtrArray *servers; - gchar *server; - GList *l; - 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)); +} - servers = g_ptr_array_new (); +const gchar ** +realm_discovery_get_strings (GHashTable *discovery, + const gchar *type) +{ + GVariant *variant; - for (l = targets; l != NULL; l = g_list_next (l)) { - server = g_strdup_printf ("%s:%d", g_srv_target_get_hostname (l->data), - (int)g_srv_target_get_port (l->data)); - g_ptr_array_add (servers, g_variant_new_string (server)); - } + g_return_val_if_fail (type != NULL, NULL); - realm_discovery_add_variant (discovery, type, - g_variant_new_array (G_VARIANT_TYPE_STRING, - (GVariant * const*)servers->pdata, - servers->len)); + if (discovery == NULL) + return NULL; - g_ptr_array_free (servers, TRUE); -} + variant = g_hash_table_lookup (discovery, type); + if (variant == NULL) + return NULL; -GVariant * -realm_discovery_to_variant (GHashTable *discovery) -{ - GPtrArray *entries; - GHashTableIter iter; - GVariant *result; - gpointer key, value; - GVariant *entry; - - entries = g_ptr_array_new (); - - if (discovery != NULL) { - g_hash_table_iter_init (&iter, discovery); - while (g_hash_table_iter_next (&iter, &key, &value)) { - entry = g_variant_new_dict_entry (g_variant_new_string (key), - g_variant_new_variant (value)); - g_ptr_array_add (entries, entry); - } - } - - result = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), - (GVariant * const *)entries->pdata, - entries->len); - - g_ptr_array_free (entries, TRUE); - return result; + return g_variant_get_strv (variant, NULL); } diff --git a/service/realm-discovery.h b/service/realm-discovery.h index 08ff912..4f35c86 100644 --- a/service/realm-discovery.h +++ b/service/realm-discovery.h @@ -38,11 +38,12 @@ void realm_discovery_add_variant (GHashTable *discovery, const gchar *type, GVariant *value); -void realm_discovery_add_srv_targets (GHashTable *discovery, +void realm_discovery_add_strings (GHashTable *discovery, const gchar *type, - GList *targets); + const char **strings); -GVariant * realm_discovery_to_variant (GHashTable *discovery); +const gchar ** realm_discovery_get_strings (GHashTable *discovery, + const gchar *type); G_END_DECLS diff --git a/service/realm-ini-config.c b/service/realm-ini-config.c index 7388f47..61f9d01 100644 --- a/service/realm-ini-config.c +++ b/service/realm-ini-config.c @@ -17,10 +17,13 @@ #include "realm-settings.h" #include "realm-ini-config.h" +#include + #include #include #include +#include #define REALM_INI_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), REALM_TYPE_INI_CONFIG, RealmIniConfigClass)) #define REALM_IS_INI_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), REALM_TYPE_INI_CONFIG)) @@ -704,6 +707,52 @@ realm_ini_config_write_file (RealmIniConfig *self, return ret; } +gboolean +realm_ini_config_write_fd (RealmIniConfig *self, + gint fd, + GError **error) +{ + GBytes *bytes; + gboolean ret = TRUE; + const gchar *contents; + gint result; + gsize length; + gint errn; + + g_return_val_if_fail (REALM_IS_INI_CONFIG (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + bytes = realm_ini_config_write_bytes (self); + g_return_val_if_fail (bytes != NULL, FALSE); + + contents = g_bytes_get_data (bytes, &length); + + while (length > 0) { + result = write (fd, contents, length); + if (result < 0) { + if (errno != EINTR && errno != EAGAIN) { + errn = errno; + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errn), + _("Couldn't write out config: %s"), g_strerror (errn)); + ret = FALSE; + break; + } + + result = 0; + } + + g_return_val_if_fail (result <= length, FALSE); + contents += result; + length -= result; + } + + g_bytes_unref (bytes); + + if (ret) + realm_ini_config_set_filename (self, NULL); + return ret; +} + static void config_set_value (RealmIniConfig *self, const gchar *section, diff --git a/service/realm-ini-config.h b/service/realm-ini-config.h index 70f3e60..e3c9a9d 100644 --- a/service/realm-ini-config.h +++ b/service/realm-ini-config.h @@ -71,6 +71,10 @@ gboolean realm_ini_config_write_file (RealmIniConfig *s const gchar *filename, GError **error); +gboolean realm_ini_config_write_fd (RealmIniConfig *self, + gint fd, + GError **error); + void realm_ini_config_set (RealmIniConfig *self, const gchar *section, const gchar *name, diff --git a/service/realm-kerberos-discover.c b/service/realm-kerberos-discover.c index 88dc5d9..c9ca93c 100644 --- a/service/realm-kerberos-discover.c +++ b/service/realm-kerberos-discover.c @@ -503,8 +503,10 @@ realm_kerberos_discover_finish (GAsyncResult *result, 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); @@ -535,9 +537,16 @@ realm_kerberos_discover_finish (GAsyncResult *result, /* The realm */ realm_discovery_add_string (*discovery, REALM_DBUS_DISCOVERY_REALM, realm); - /* The servers */ - realm_discovery_add_srv_targets (*discovery, REALM_DBUS_DISCOVERY_KDCS, - self->servers); + /* The 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) { diff --git a/service/realm-samba-enroll.c b/service/realm-samba-enroll.c index 500da5f..890129d 100644 --- a/service/realm-samba-enroll.c +++ b/service/realm-samba-enroll.c @@ -16,8 +16,11 @@ #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-kerberos-discover.h" #include "realm-samba-config.h" #include "realm-samba-enroll.h" #include "realm-samba-provider.h" @@ -35,10 +38,11 @@ typedef struct { GDBusMethodInvocation *invocation; gchar *create_computer_arg; - GHashTable *settings; gchar *realm; gchar *user_name; GBytes *password_input; + RealmIniConfig *config; + gchar *custom_smb_conf; } JoinClosure; static void @@ -50,9 +54,13 @@ join_closure_free (gpointer data) g_free (join->user_name); g_free (join->create_computer_arg); g_free (join->realm); - if (join->settings) - g_hash_table_unref (join->settings); g_clear_object (&join->invocation); + g_clear_object (&join->config); + + if (join->custom_smb_conf) { + g_unlink (join->custom_smb_conf); + g_free (join->custom_smb_conf); + } g_slice_free (JoinClosure, join); } @@ -74,6 +82,11 @@ join_closure_init (const gchar *realm, join->user_name = g_strdup (user_name); + join->config = realm_ini_config_new (REALM_INI_NO_WATCH | REALM_INI_PRIVATE); + realm_ini_config_set (join->config, REALM_SAMBA_CONFIG_GLOBAL, "security", "ads"); + realm_ini_config_set (join->config, REALM_SAMBA_CONFIG_GLOBAL, "kerberos method", "system keytab"); + realm_ini_config_set (join->config, REALM_SAMBA_CONFIG_GLOBAL, "realm", join->realm); + return join; } @@ -100,8 +113,10 @@ begin_net_process (JoinClosure *join, /* Use our custom smb.conf */ g_ptr_array_add (args, (gpointer)realm_settings_path ("net")); - g_ptr_array_add (args, "-s"); - g_ptr_array_add (args, PRIVATE_DIR "/net-ads-smb.conf"); + if (join->custom_smb_conf) { + g_ptr_array_add (args, "-s"); + g_ptr_array_add (args, join->custom_smb_conf); + } va_start (va, user_data); do { @@ -117,50 +132,11 @@ begin_net_process (JoinClosure *join, } static void -on_list_complete (GObject *source, - GAsyncResult *result, - gpointer user_data) +on_keytab_do_finish (GObject *source, + GAsyncResult *result, + gpointer user_data) { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); - JoinClosure *join = g_simple_async_result_get_op_res_gpointer (res); - GString *output = NULL; - RealmIniConfig *config; - GError *error = NULL; - gint status; - - status = realm_command_run_finish (result, &output, &error); - if (error == NULL && status != 0) - g_set_error (&error, REALM_ERROR, REALM_ERROR_INTERNAL, - "Listing samba registry failed"); - - if (error == NULL) { - /* Read the command output as a samba config */ - config = realm_ini_config_new (REALM_INI_LINE_CONTINUATIONS); - realm_ini_config_read_string (config, output->str); - join->settings = realm_ini_config_get_all (config, REALM_SAMBA_CONFIG_GLOBAL); - g_hash_table_insert (join->settings, - g_strdup ("kerberos method"), - g_strdup ("secrets and keytab")); - - g_object_unref (config); - } - - if (error != NULL) - g_simple_async_result_take_error (res, error); - - if (output) - g_string_free (output, TRUE); - g_simple_async_result_complete (res); - g_object_unref (res); -} - -static void -on_keytab_do_list (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); - JoinClosure *join = g_simple_async_result_get_op_res_gpointer (res); GError *error = NULL; gint status; @@ -169,21 +145,9 @@ on_keytab_do_list (GObject *source, g_set_error (&error, REALM_ERROR, REALM_ERROR_INTERNAL, "Extracting host keytab failed"); - /* - * So at this point we're done joining, and want to get some settings - * that the net process wrote to the registry, and put them in the - * main smb.conf - */ - if (error == NULL) { - begin_net_process (join, NULL, - on_list_complete, g_object_ref (res), - "conf", "list", NULL); - - } else { + if (error != NULL) g_simple_async_result_take_error (res, error); - g_simple_async_result_complete (res); - } - + g_simple_async_result_complete (res); g_object_unref (res); } @@ -242,7 +206,7 @@ on_join_do_keytab (GObject *source, if (error == NULL) { begin_net_process (join, join->password_input, - on_keytab_do_list, g_object_ref (res), + on_keytab_do_finish, g_object_ref (res), "-U", join->user_name, "ads", "keytab", "create", NULL); } else { g_simple_async_result_take_error (res, error); @@ -252,63 +216,163 @@ on_join_do_keytab (GObject *source, g_object_unref (res); } +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 -on_conf_kerberos_method_do_join (GObject *source, - GAsyncResult *result, - gpointer user_data) +begin_config_and_join (JoinClosure *join, + GSimpleAsyncResult *async) { - GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); - JoinClosure *join = g_simple_async_result_get_op_res_gpointer (res); GError *error = NULL; - gint status; + gchar *workgroup; + gint temp_fd; - status = realm_command_run_finish (result, NULL, &error); - if (error == NULL && status != 0) { - g_set_error (&error, REALM_ERROR, REALM_ERROR_INTERNAL, - "Configuring samba failed"); + /* + * 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); } + 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); + + realm_ini_config_write_fd (join->config, temp_fd, &error); + close (temp_fd); if (error == NULL) { begin_net_process (join, join->password_input, - on_join_do_keytab, g_object_ref (res), + on_join_do_keytab, g_object_ref (async), "-U", join->user_name, "ads", "join", join->realm, join->create_computer_arg, NULL); } else { - g_simple_async_result_take_error (res, error); - g_simple_async_result_complete (res); + g_simple_async_result_take_error (async, error); + g_simple_async_result_complete (async); } - g_object_unref (res); +} + +static gchar * +find_workgroup_in_lookup_output (GString *output) +{ + const gchar *match = "Pre-Win2k Domain:"; + 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_conf_realm_do_kerberos_method (GObject *source, - GAsyncResult *result, - gpointer user_data) +on_net_ads_lookup (GObject *source, + GAsyncResult *result, + gpointer user_data) { - GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); - JoinClosure *join = g_simple_async_result_get_op_res_gpointer (res); + GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data); + JoinClosure *join = g_simple_async_result_get_op_res_gpointer (async); GError *error = NULL; + GString *output = NULL; + gchar *workgroup; gint status; - status = realm_command_run_finish (result, NULL, &error); + status = realm_command_run_finish (result, &output, &error); if (error == NULL && status != 0) { g_set_error (&error, REALM_ERROR, REALM_ERROR_INTERNAL, - "Configuring samba failed"); + "Couldn't lookup domain info"); } if (error == NULL) { + workgroup = find_workgroup_in_lookup_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); + 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, async); + + g_object_unref (async); +} + + +static void +begin_net_lookup (JoinClosure *join, + GSimpleAsyncResult *async, + 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_conf_kerberos_method_do_join, g_object_ref (res), - "conf", "setparm", REALM_SAMBA_CONFIG_GLOBAL, - "kerberos method", "system keytab", NULL); + on_net_ads_lookup, g_object_ref (async), + "ads", "lookup", "-S", kdcs[0], NULL); + } else { - g_simple_async_result_take_error (res, error); - g_simple_async_result_complete (res); + begin_config_and_join (join, async); } +} - g_object_unref (res); +static void +on_discover_do_lookup (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data); + JoinClosure *join = g_simple_async_result_get_op_res_gpointer (async); + GError *error = NULL; + GHashTable *discovery; + + realm_kerberos_discover_finish (result, &discovery, &error); + if (error == NULL) { + begin_net_lookup (join, async, discovery); + g_hash_table_unref (discovery); + + } else { + g_simple_async_result_take_error (async, error); + g_simple_async_result_complete (async); + } + + g_object_unref (async); } void @@ -316,6 +380,7 @@ realm_samba_enroll_join_async (const gchar *realm, const gchar *user_name, GBytes *password, const gchar *computer_ou, + GHashTable *discovery, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, gpointer user_data) @@ -351,11 +416,13 @@ realm_samba_enroll_join_async (const gchar *realm, if (error != NULL) { g_simple_async_result_take_error (res, error); g_simple_async_result_complete_in_idle (res); + + } else if (discovery) { + begin_net_lookup (join, res, discovery); + } else { - begin_net_process (join, NULL, - on_conf_realm_do_kerberos_method, g_object_ref (res), - "conf", "setparm", REALM_SAMBA_CONFIG_GLOBAL, - "realm", join->realm, NULL); + realm_kerberos_discover_async (join->realm, join->invocation, + on_discover_do_lookup, g_object_ref (res)); } g_object_unref (res); @@ -376,10 +443,7 @@ realm_samba_enroll_join_finish (GAsyncResult *result, if (settings != NULL) { join = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); - if (join->settings) - *settings = g_hash_table_ref (join->settings); - else - *settings = NULL; + *settings = realm_ini_config_get_all (join->config, REALM_SAMBA_CONFIG_GLOBAL); } return TRUE; diff --git a/service/realm-samba-enroll.h b/service/realm-samba-enroll.h index f7c3a23..3b44d8f 100644 --- a/service/realm-samba-enroll.h +++ b/service/realm-samba-enroll.h @@ -27,6 +27,7 @@ void realm_samba_enroll_join_async (const gchar *realm, const gchar *user_name, GBytes *password, const gchar *computer_ou, + GHashTable *discovery, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, gpointer user_data); diff --git a/service/realm-samba.c b/service/realm-samba.c index 643c909..7dde4ac 100644 --- a/service/realm-samba.c +++ b/service/realm-samba.c @@ -21,6 +21,7 @@ #include "realm-discovery.h" #include "realm-errors.h" #include "realm-kerberos.h" +#include "realm-kerberos-discover.h" #include "realm-kerberos-membership.h" #include "realm-packages.h" #include "realm-provider.h" @@ -243,19 +244,21 @@ on_install_do_join (GObject *source, { GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); EnrollClosure *enroll = g_simple_async_result_get_op_res_gpointer (res); + RealmKerberos *kerberos = REALM_KERBEROS (g_async_result_get_source_object (user_data)); GError *error = NULL; realm_packages_install_finish (result, &error); if (error == NULL) { realm_samba_enroll_join_async (enroll->realm_name, enroll->user_name, enroll->password, - enroll->computer_ou, enroll->invocation, - on_join_do_winbind, g_object_ref (res)); + enroll->computer_ou, realm_kerberos_get_discovery (kerberos), + enroll->invocation, on_join_do_winbind, g_object_ref (res)); } else { g_simple_async_result_take_error (res, error); g_simple_async_result_complete (res); } + g_object_unref (kerberos); g_object_unref (res); } diff --git a/service/realm-sssd-ad.c b/service/realm-sssd-ad.c index b4534a0..1fe1281 100644 --- a/service/realm-sssd-ad.c +++ b/service/realm-sssd-ad.c @@ -307,6 +307,7 @@ on_install_do_join (GObject *source, { GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data); JoinClosure *join = g_simple_async_result_get_op_res_gpointer (async); + RealmKerberos *kerberos = REALM_KERBEROS (g_async_result_get_source_object (user_data)); GError *error = NULL; realm_packages_install_finish (result, &error); @@ -339,8 +340,8 @@ on_install_do_join (GObject *source, g_assert (join->user_name != NULL); g_assert (join->user_password != NULL); realm_samba_enroll_join_async (join->realm_name, join->user_name, join->user_password, - join->computer_ou, join->invocation, - on_join_do_sssd, g_object_ref (async)); + join->computer_ou, realm_kerberos_get_discovery (kerberos), + join->invocation, on_join_do_sssd, g_object_ref (async)); } } else { @@ -348,6 +349,7 @@ on_install_do_join (GObject *source, g_simple_async_result_complete (async); } + g_object_unref (kerberos); g_object_unref (async); } -- 1.8.1.4