1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3
* Copyright 2009-2012 Red Hat, Inc.
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
* Written by: Matthias Clasen <mclasen@redhat.com>
20
* Stef Walter <stefw@gnome.org>
25
#include "um-realm-manager.h"
27
#include <krb5/krb5.h>
30
#include <glib/gi18n.h>
31
#include <glib/gstdio.h>
33
#include <sys/types.h>
39
struct _UmRealmManager {
40
UmRealmObjectManagerClient parent;
41
UmRealmProvider *provider;
42
guint diagnostics_sig;
46
UmRealmProviderProxyClass parent_class;
47
} UmRealmManagerClass;
54
static gint signals[NUM_SIGNALS] = { 0, };
56
G_DEFINE_TYPE (UmRealmManager, um_realm_manager, UM_REALM_TYPE_OBJECT_MANAGER_CLIENT);
59
um_realm_error_get_quark (void)
61
static GQuark quark = 0;
63
quark = g_quark_from_static_string ("um-realm-error");
68
is_realm_with_kerberos_and_membership (gpointer object)
70
GDBusInterface *interface;
72
if (!G_IS_DBUS_OBJECT (object))
75
interface = g_dbus_object_get_interface (object, "org.freedesktop.realmd.Kerberos");
76
if (interface == NULL)
78
g_object_unref (interface);
80
interface = g_dbus_object_get_interface (object, "org.freedesktop.realmd.KerberosMembership");
81
if (interface == NULL)
83
g_object_unref (interface);
89
on_interface_added (GDBusObjectManager *manager,
91
GDBusInterface *interface)
93
g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (interface), G_MAXINT);
97
on_object_added (GDBusObjectManager *manager,
101
if (is_realm_with_kerberos_and_membership (object))
102
g_signal_emit (user_data, signals[REALM_ADDED], 0, object);
106
um_realm_manager_init (UmRealmManager *self)
108
g_signal_connect (self, "object-added", G_CALLBACK (on_object_added), self);
109
g_signal_connect (self, "interface-added", G_CALLBACK (on_interface_added), self);
113
um_realm_manager_dispose (GObject *obj)
115
UmRealmManager *self = UM_REALM_MANAGER (obj);
116
GDBusConnection *connection;
118
g_clear_object (&self->provider);
120
if (self->diagnostics_sig) {
121
connection = g_dbus_object_manager_client_get_connection (G_DBUS_OBJECT_MANAGER_CLIENT (self));
122
if (connection != NULL)
123
g_dbus_connection_signal_unsubscribe (connection, self->diagnostics_sig);
124
self->diagnostics_sig = 0;
127
G_OBJECT_CLASS (um_realm_manager_parent_class)->dispose (obj);
131
um_realm_manager_class_init (UmRealmManagerClass *klass)
133
GObjectClass *object_class = G_OBJECT_CLASS (klass);
135
object_class->dispose = um_realm_manager_dispose;
137
signals[REALM_ADDED] = g_signal_new ("realm-added", UM_TYPE_REALM_MANAGER,
138
G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
139
g_cclosure_marshal_generic,
140
G_TYPE_NONE, 1, UM_REALM_TYPE_OBJECT);
144
on_realm_diagnostics (GDBusConnection *connection,
145
const gchar *sender_name,
146
const gchar *object_path,
147
const gchar *interface_name,
148
const gchar *signal_name,
149
GVariant *parameters,
152
const gchar *message;
155
if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(ss)"))) {
156
/* Data is already formatted appropriately for stderr */
157
g_variant_get (parameters, "(&s&s)", &message, &unused);
158
g_printerr ("%s", message);
163
number_at_least (const gchar *number,
168
if (strtol (number, &end, 10) < (long)minimum)
170
if (!end || *end != '\0')
176
version_compare (const char *version,
180
gboolean match = FALSE;
183
parts = g_strsplit (version, ".", 2);
185
if (parts[0] && parts[1]) {
186
match = number_at_least (parts[0], req_major) &&
187
number_at_least (parts[1], req_minor);
195
um_realm_manager_new (GCancellable *cancellable,
196
GAsyncReadyCallback callback,
199
g_async_initable_new_async (UM_TYPE_REALM_MANAGER, G_PRIORITY_DEFAULT,
200
cancellable, callback, user_data,
201
"flags", G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
202
"name", "org.freedesktop.realmd",
203
"bus-type", G_BUS_TYPE_SYSTEM,
204
"object-path", "/org/freedesktop/realmd",
205
"get-proxy-type-func", um_realm_object_manager_client_get_proxy_type,
210
um_realm_manager_new_finish (GAsyncResult *result,
213
UmRealmManager *self;
214
GDBusConnection *connection;
215
GObject *source_object;
216
const gchar *version;
220
source_object = g_async_result_get_source_object (result);
221
ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, error);
222
g_object_unref (source_object);
227
self = UM_REALM_MANAGER (ret);
228
connection = g_dbus_object_manager_client_get_connection (G_DBUS_OBJECT_MANAGER_CLIENT (self));
231
* TODO: Remove this version checking. This is temporary code, so
232
* just use sync here. Shortly we'll be stabilizing the realmd
236
self->provider = um_realm_provider_proxy_new_sync (connection, G_DBUS_PROXY_FLAGS_NONE,
237
"org.freedesktop.realmd",
238
"/org/freedesktop/realmd",
240
if (self->provider == NULL) {
241
g_object_unref (self);
245
version = um_realm_provider_get_version (self->provider);
246
if (version == NULL || !version_compare (version, 0, 7)) {
247
/* No need to bother translators with this temporary message */
248
g_set_error (error, UM_REALM_ERROR, UM_REALM_ERROR_GENERIC,
249
"realmd version should be at least 0.7");
250
g_object_unref (self);
254
sig = g_dbus_connection_signal_subscribe (connection,
255
"org.freedesktop.realmd",
256
"org.freedesktop.realmd.Service",
260
G_DBUS_SIGNAL_FLAGS_NONE,
261
on_realm_diagnostics,
264
self->diagnostics_sig = sig;
270
GDBusObjectManager *manager;
271
GCancellable *cancellable;
276
discover_closure_free (gpointer data)
278
DiscoverClosure *discover = data;
279
g_object_unref (discover->manager);
280
g_clear_object (&discover->cancellable);
281
g_list_free_full (discover->realms, g_object_unref);
282
g_slice_free (DiscoverClosure, discover);
286
on_provider_discover (GObject *source,
287
GAsyncResult *result,
290
GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
291
DiscoverClosure *discover = g_simple_async_result_get_op_res_gpointer (async);
293
GError *error = NULL;
298
um_realm_provider_call_discover_finish (UM_REALM_PROVIDER (source), &relevance,
299
&realms, result, &error);
301
for (i = 0; realms[i]; i++) {
302
object = g_dbus_object_manager_get_object (discover->manager, realms[i]);
304
g_warning ("Realm is not in object manager: %s", realms[i]);
306
discover->realms = g_list_prepend (discover->realms, object);
310
g_simple_async_result_take_error (async, error);
311
g_simple_async_result_complete (async);
314
g_object_unref (async);
318
um_realm_manager_discover (UmRealmManager *self,
320
GCancellable *cancellable,
321
GAsyncReadyCallback callback,
324
GSimpleAsyncResult *res;
325
DiscoverClosure *discover;
328
g_return_if_fail (UM_IS_REALM_MANAGER (self));
329
g_return_if_fail (input != NULL);
330
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
332
res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
333
um_realm_manager_discover);
334
discover = g_slice_new0 (DiscoverClosure);
335
discover->manager = g_object_ref (self);
336
discover->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
337
g_simple_async_result_set_op_res_gpointer (res, discover, discover_closure_free);
339
options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
341
um_realm_provider_call_discover (self->provider, input, options, cancellable,
342
on_provider_discover, g_object_ref (res));
344
g_object_unref (res);
348
um_realm_manager_discover_finish (UmRealmManager *self,
349
GAsyncResult *result,
352
GSimpleAsyncResult *async;
353
DiscoverClosure *discover;
356
g_return_val_if_fail (UM_IS_REALM_MANAGER (self), NULL);
357
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
358
um_realm_manager_discover), NULL);
359
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
361
async = G_SIMPLE_ASYNC_RESULT (result);
362
if (g_simple_async_result_propagate_error (async, error))
365
discover = g_simple_async_result_get_op_res_gpointer (async);
366
if (!discover->realms) {
367
g_set_error (error, UM_REALM_ERROR, UM_REALM_ERROR_GENERIC,
368
_("No such domain or realm found"));
372
realms = g_list_reverse (discover->realms);
373
discover->realms = NULL;
378
um_realm_manager_get_realms (UmRealmManager *self)
381
GList *realms = NULL;
384
g_return_val_if_fail (UM_IS_REALM_MANAGER (self), NULL);
386
objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self));
387
for (l = objects; l != NULL; l = g_list_next (l)) {
388
if (is_realm_with_kerberos_and_membership (l->data))
389
realms = g_list_prepend (realms, g_object_ref (l->data));
392
g_list_free_full (objects, g_object_unref);
397
string_replace (GString *string,
399
const gchar *replace)
404
at = strstr (string->str, find);
406
pos = at - string->str;
407
g_string_erase (string, pos, strlen (find));
408
g_string_insert (string, pos, replace);
413
um_realm_calculate_login (UmRealmCommon *realm,
414
const gchar *username)
417
const gchar *const *formats;
420
formats = um_realm_common_get_login_formats (realm);
421
if (formats[0] != NULL) {
422
string = g_string_new (formats[0]);
423
string_replace (string, "%U", username);
424
string_replace (string, "%D", um_realm_common_get_name (realm));
425
login = g_string_free (string, FALSE);
433
um_realm_is_configured (UmRealmObject *realm)
435
UmRealmCommon *common;
436
const gchar *configured;
439
common = um_realm_object_get_common (realm);
440
configured = um_realm_common_get_configured (common);
441
is = configured != NULL && !g_str_equal (configured, "");
442
g_object_unref (common);
448
find_supported_credentials (UmRealmKerberosMembership *membership,
451
const gchar *cred_owner;
452
const gchar *cred_type;
456
supported = um_realm_kerberos_membership_get_supported_join_credentials (membership);
457
g_return_val_if_fail (supported != NULL, NULL);
459
g_variant_iter_init (&iter, supported);
460
while (g_variant_iter_loop (&iter, "(&s&s)", &cred_type, &cred_owner)) {
461
if (g_str_equal (owner, cred_owner)) {
462
if (g_str_equal (cred_type, "ccache") ||
463
g_str_equal (cred_type, "password")) {
464
return g_intern_string (cred_type);
473
on_realm_join_complete (GObject *source,
474
GAsyncResult *result,
477
GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
478
g_simple_async_result_set_op_res_gpointer (async, g_object_ref (result), g_object_unref);
479
g_simple_async_result_complete_in_idle (async);
480
g_object_unref (async);
484
realm_join_as_owner (UmRealmObject *realm,
487
const gchar *password,
489
GCancellable *cancellable,
490
GAsyncReadyCallback callback,
493
UmRealmKerberosMembership *membership;
494
GSimpleAsyncResult *async;
500
membership = um_realm_object_get_kerberos_membership (realm);
501
g_return_val_if_fail (membership != NULL, FALSE);
503
type = find_supported_credentials (membership, owner);
505
g_object_unref (membership);
509
async = g_simple_async_result_new (G_OBJECT (realm), callback, user_data,
510
realm_join_as_owner);
512
if (g_str_equal (type, "ccache")) {
513
contents = g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
514
g_bytes_get_data (credentials, NULL),
515
g_bytes_get_size (credentials),
516
TRUE, (GDestroyNotify)g_bytes_unref, credentials);
518
} else if (g_str_equal (type, "password")) {
519
contents = g_variant_new ("(ss)", login, password);
522
g_assert_not_reached ();
525
creds = g_variant_new ("(ssv)", type, owner, contents);
526
options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
528
um_realm_kerberos_membership_call_join (membership, creds, options,
529
cancellable, on_realm_join_complete,
530
g_object_ref (async));
532
g_object_unref (async);
533
g_object_unref (membership);
539
um_realm_join_as_user (UmRealmObject *realm,
541
const gchar *password,
543
GCancellable *cancellable,
544
GAsyncReadyCallback callback,
547
g_return_val_if_fail (UM_REALM_IS_OBJECT (realm), FALSE);
548
g_return_val_if_fail (credentials != NULL, FALSE);
549
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
550
g_return_val_if_fail (login != NULL, FALSE);
551
g_return_val_if_fail (password != NULL, FALSE);
552
g_return_val_if_fail (credentials != NULL, FALSE);
554
return realm_join_as_owner (realm, "user", login, password,
555
credentials, cancellable, callback, user_data);
559
um_realm_join_as_admin (UmRealmObject *realm,
561
const gchar *password,
563
GCancellable *cancellable,
564
GAsyncReadyCallback callback,
567
g_return_val_if_fail (UM_REALM_IS_OBJECT (realm), FALSE);
568
g_return_val_if_fail (credentials != NULL, FALSE);
569
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
570
g_return_val_if_fail (login != NULL, FALSE);
571
g_return_val_if_fail (password != NULL, FALSE);
572
g_return_val_if_fail (credentials != NULL, FALSE);
574
return realm_join_as_owner (realm, "administrator", login, password, credentials,
575
cancellable, callback, user_data);
579
um_realm_join_finish (UmRealmObject *realm,
580
GAsyncResult *result,
583
UmRealmKerberosMembership *membership;
584
GError *call_error = NULL;
588
g_return_val_if_fail (UM_REALM_IS_OBJECT (realm), FALSE);
589
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
591
membership = um_realm_object_get_kerberos_membership (realm);
592
g_return_val_if_fail (membership != NULL, FALSE);
594
async = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
595
um_realm_kerberos_membership_call_join_finish (membership, async, &call_error);
596
g_object_unref (membership);
598
if (call_error == NULL)
601
dbus_error = g_dbus_error_get_remote_error (call_error);
602
if (dbus_error == NULL) {
603
g_propagate_error (error, call_error);
607
g_dbus_error_strip_remote_error (call_error);
609
if (g_str_equal (dbus_error, "org.freedesktop.realmd.Error.AuthenticationFailed")) {
610
g_set_error (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN,
611
"%s", call_error->message);
612
g_error_free (call_error);
614
g_propagate_error (error, call_error);
630
login_closure_free (gpointer data)
632
LoginClosure *login = data;
633
g_free (login->domain);
634
g_free (login->realm);
635
g_free (login->user);
636
g_free (login->password);
637
g_bytes_unref (login->credentials);
638
g_slice_free (LoginClosure, login);
641
static krb5_error_code
642
login_perform_kinit (krb5_context k5,
645
const gchar *password,
646
const gchar *filename)
648
krb5_get_init_creds_opt *opts;
649
krb5_error_code code;
650
krb5_principal principal;
655
name = g_strdup_printf ("%s@%s", login, realm);
656
code = krb5_parse_name (k5, name, &principal);
662
if (filename == NULL)
663
code = krb5_cc_default (k5, &ccache);
665
code = krb5_cc_resolve (k5, filename, &ccache);
668
krb5_free_principal (k5, principal);
672
code = krb5_get_init_creds_opt_alloc (k5, &opts);
673
g_return_val_if_fail (code == 0, code);
675
code = krb5_get_init_creds_opt_set_out_ccache (k5, opts, ccache);
676
g_return_val_if_fail (code == 0, code);
678
code = krb5_get_init_creds_password (k5, &creds, principal,
680
NULL, 0, 0, NULL, opts);
682
krb5_get_init_creds_opt_free (k5, opts);
683
krb5_cc_close (k5, ccache);
684
krb5_free_principal (k5, principal);
687
krb5_free_cred_contents (k5, &creds);
693
kinit_thread_func (GSimpleAsyncResult *async,
695
GCancellable *cancellable)
697
LoginClosure *login = g_simple_async_result_get_op_res_gpointer (async);
698
krb5_context k5 = NULL;
699
krb5_error_code code;
700
GError *error = NULL;
701
gchar *filename = NULL;
706
filename = g_build_filename (g_get_user_runtime_dir (),
707
"um-krb5-creds.XXXXXX", NULL);
708
temp_fd = g_mkstemp_full (filename, O_RDWR, S_IRUSR | S_IWUSR);
710
g_warning ("Couldn't create credential cache file: %s: %s",
711
filename, g_strerror (errno));
718
code = krb5_init_context (&k5);
720
code = login_perform_kinit (k5, login->realm, login->user,
721
login->password, filename);
726
if (filename != NULL) {
727
g_file_get_contents (filename, &contents, &length, &error);
729
login->credentials = g_bytes_new_take (contents, length);
731
g_warning ("Couldn't read credential cache: %s", error->message);
732
g_error_free (error);
737
case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
738
case KRB5KDC_ERR_CLIENT_REVOKED:
739
case KRB5KDC_ERR_KEY_EXP:
740
case KRB5KDC_ERR_POLICY:
741
case KRB5KDC_ERR_ETYPE_NOSUPP:
742
g_simple_async_result_set_error (async, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN,
743
_("Cannot log in as %s at the %s domain"),
744
login->user, login->domain);
746
case KRB5KDC_ERR_PREAUTH_FAILED:
747
g_simple_async_result_set_error (async, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD,
748
_("Invalid password, please try again"));
751
g_simple_async_result_set_error (async, UM_REALM_ERROR, UM_REALM_ERROR_GENERIC,
752
_("Couldn't connect to the %s domain: %s"),
753
login->domain, krb5_get_error_message (k5, code));
763
krb5_free_context (k5);
767
um_realm_login (UmRealmObject *realm,
769
const gchar *password,
770
GCancellable *cancellable,
771
GAsyncReadyCallback callback,
774
GSimpleAsyncResult *async;
776
UmRealmKerberos *kerberos;
778
g_return_if_fail (UM_REALM_IS_OBJECT (realm));
779
g_return_if_fail (user != NULL);
780
g_return_if_fail (password != NULL);
781
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
783
kerberos = um_realm_object_get_kerberos (realm);
784
g_return_if_fail (kerberos != NULL);
786
async = g_simple_async_result_new (NULL, callback, user_data,
788
login = g_slice_new0 (LoginClosure);
789
login->domain = g_strdup (um_realm_kerberos_get_domain_name (kerberos));
790
login->realm = g_strdup (um_realm_kerberos_get_realm_name (kerberos));
791
login->user = g_strdup (user);
792
login->password = g_strdup (password);
793
g_simple_async_result_set_op_res_gpointer (async, login, login_closure_free);
795
g_simple_async_result_set_handle_cancellation (async, TRUE);
796
g_simple_async_result_run_in_thread (async, kinit_thread_func,
797
G_PRIORITY_DEFAULT, cancellable);
799
g_object_unref (async);
800
g_object_unref (kerberos);
804
um_realm_login_finish (GAsyncResult *result,
805
GBytes **credentials,
808
GSimpleAsyncResult *async;
811
g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
812
um_realm_login), FALSE);
813
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
815
async = G_SIMPLE_ASYNC_RESULT (result);
816
if (g_simple_async_result_propagate_error (async, error))
819
login = g_simple_async_result_get_op_res_gpointer (async);
821
if (login->credentials)
822
*credentials = g_bytes_ref (login->credentials);