~ubuntu-branches/ubuntu/saucy/gnome-online-accounts/saucy

« back to all changes in this revision

Viewing changes to src/goaidentity/um-realm-manager.c

  • Committer: Package Import Robot
  • Author(s): Jeremy Bicha
  • Date: 2012-08-20 22:37:44 UTC
  • mfrom: (1.1.15)
  • Revision ID: package-import@ubuntu.com-20120820223744-ndi1s9124mef09ak
Tags: 3.5.90-0ubuntu1
* New upstream release.
* debian/control.in:
  - Build-depend on krb5-config, libgcr-3-dev, and libkrb5-dev
* debian/rules:
  - Enable Kerberos provider
* debian/libgoa-1.0-0.symbols: Added new symbols

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 
2
/*
 
3
 * Copyright 2009-2012  Red Hat, Inc.
 
4
 *
 
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.
 
9
 *
 
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.
 
14
 *
 
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.
 
18
 *
 
19
 * Written by: Matthias Clasen <mclasen@redhat.com>
 
20
 *             Stef Walter <stefw@gnome.org>
 
21
 */
 
22
 
 
23
#include "config.h"
 
24
 
 
25
#include "um-realm-manager.h"
 
26
 
 
27
#include <krb5/krb5.h>
 
28
 
 
29
#include <glib.h>
 
30
#include <glib/gi18n.h>
 
31
#include <glib/gstdio.h>
 
32
 
 
33
#include <sys/types.h>
 
34
#include <sys/stat.h>
 
35
#include <errno.h>
 
36
#include <fcntl.h>
 
37
 
 
38
 
 
39
struct _UmRealmManager {
 
40
        UmRealmObjectManagerClient parent;
 
41
        UmRealmProvider *provider;
 
42
        guint diagnostics_sig;
 
43
};
 
44
 
 
45
typedef struct {
 
46
        UmRealmProviderProxyClass parent_class;
 
47
} UmRealmManagerClass;
 
48
 
 
49
enum {
 
50
        REALM_ADDED,
 
51
        NUM_SIGNALS,
 
52
};
 
53
 
 
54
static gint signals[NUM_SIGNALS] = { 0, };
 
55
 
 
56
G_DEFINE_TYPE (UmRealmManager, um_realm_manager, UM_REALM_TYPE_OBJECT_MANAGER_CLIENT);
 
57
 
 
58
GQuark
 
59
um_realm_error_get_quark (void)
 
60
{
 
61
        static GQuark quark = 0;
 
62
        if (quark == 0)
 
63
                quark = g_quark_from_static_string ("um-realm-error");
 
64
        return quark;
 
65
}
 
66
 
 
67
static gboolean
 
68
is_realm_with_kerberos_and_membership (gpointer object)
 
69
{
 
70
        GDBusInterface *interface;
 
71
 
 
72
        if (!G_IS_DBUS_OBJECT (object))
 
73
                return FALSE;
 
74
 
 
75
        interface = g_dbus_object_get_interface (object, "org.freedesktop.realmd.Kerberos");
 
76
        if (interface == NULL)
 
77
                return FALSE;
 
78
        g_object_unref (interface);
 
79
 
 
80
        interface = g_dbus_object_get_interface (object, "org.freedesktop.realmd.KerberosMembership");
 
81
        if (interface == NULL)
 
82
                return FALSE;
 
83
        g_object_unref (interface);
 
84
 
 
85
        return TRUE;
 
86
}
 
87
 
 
88
static void
 
89
on_interface_added (GDBusObjectManager *manager,
 
90
                    GDBusObject *object,
 
91
                    GDBusInterface *interface)
 
92
{
 
93
        g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (interface), G_MAXINT);
 
94
}
 
95
 
 
96
static void
 
97
on_object_added (GDBusObjectManager *manager,
 
98
                 GDBusObject *object,
 
99
                 gpointer user_data)
 
100
{
 
101
        if (is_realm_with_kerberos_and_membership (object))
 
102
                g_signal_emit (user_data, signals[REALM_ADDED], 0, object);
 
103
}
 
104
 
 
105
static void
 
106
um_realm_manager_init (UmRealmManager *self)
 
107
{
 
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);
 
110
}
 
111
 
 
112
static void
 
113
um_realm_manager_dispose (GObject *obj)
 
114
{
 
115
        UmRealmManager *self = UM_REALM_MANAGER (obj);
 
116
        GDBusConnection *connection;
 
117
 
 
118
        g_clear_object (&self->provider);
 
119
 
 
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;
 
125
        }
 
126
 
 
127
        G_OBJECT_CLASS (um_realm_manager_parent_class)->dispose (obj);
 
128
}
 
129
 
 
130
static void
 
131
um_realm_manager_class_init (UmRealmManagerClass *klass)
 
132
{
 
133
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
134
 
 
135
        object_class->dispose = um_realm_manager_dispose;
 
136
 
 
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);
 
141
}
 
142
 
 
143
static void
 
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,
 
150
                      gpointer user_data)
 
151
{
 
152
        const gchar *message;
 
153
        const gchar *unused;
 
154
 
 
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);
 
159
        }
 
160
}
 
161
 
 
162
static gboolean
 
163
number_at_least (const gchar *number,
 
164
                 guint minimum)
 
165
{
 
166
        gchar *end;
 
167
 
 
168
        if (strtol (number, &end, 10) < (long)minimum)
 
169
                return FALSE;
 
170
        if (!end || *end != '\0')
 
171
                return FALSE;
 
172
        return TRUE;
 
173
}
 
174
 
 
175
static gboolean
 
176
version_compare (const char *version,
 
177
                 guint req_major,
 
178
                 guint req_minor)
 
179
{
 
180
        gboolean match = FALSE;
 
181
        gchar **parts;
 
182
 
 
183
        parts = g_strsplit (version, ".", 2);
 
184
 
 
185
        if (parts[0] && parts[1]) {
 
186
                match = number_at_least (parts[0], req_major) &&
 
187
                        number_at_least (parts[1], req_minor);
 
188
        }
 
189
 
 
190
        g_strfreev (parts);
 
191
        return match;
 
192
}
 
193
 
 
194
void
 
195
um_realm_manager_new (GCancellable *cancellable,
 
196
                      GAsyncReadyCallback callback,
 
197
                      gpointer user_data)
 
198
{
 
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,
 
206
                                    NULL);
 
207
}
 
208
 
 
209
UmRealmManager *
 
210
um_realm_manager_new_finish (GAsyncResult *result,
 
211
                             GError **error)
 
212
{
 
213
        UmRealmManager *self;
 
214
        GDBusConnection *connection;
 
215
        GObject *source_object;
 
216
        const gchar *version;
 
217
        GObject *ret;
 
218
        guint sig;
 
219
 
 
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);
 
223
 
 
224
        if (ret == NULL)
 
225
                return NULL;
 
226
 
 
227
        self = UM_REALM_MANAGER (ret);
 
228
        connection = g_dbus_object_manager_client_get_connection (G_DBUS_OBJECT_MANAGER_CLIENT (self));
 
229
 
 
230
        /*
 
231
         * TODO: Remove this version checking. This is temporary code, so
 
232
         * just use sync here. Shortly we'll be stabilizing the realmd
 
233
         * interfaces.
 
234
         */
 
235
 
 
236
        self->provider = um_realm_provider_proxy_new_sync (connection, G_DBUS_PROXY_FLAGS_NONE,
 
237
                                                           "org.freedesktop.realmd",
 
238
                                                           "/org/freedesktop/realmd",
 
239
                                                           NULL, error);
 
240
        if (self->provider == NULL) {
 
241
                g_object_unref (self);
 
242
                return NULL;
 
243
        }
 
244
 
 
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);
 
251
                return NULL;
 
252
        }
 
253
 
 
254
        sig = g_dbus_connection_signal_subscribe (connection,
 
255
                                                  "org.freedesktop.realmd",
 
256
                                                  "org.freedesktop.realmd.Service",
 
257
                                                  "Diagnostics",
 
258
                                                  NULL,
 
259
                                                  NULL,
 
260
                                                  G_DBUS_SIGNAL_FLAGS_NONE,
 
261
                                                  on_realm_diagnostics,
 
262
                                                  NULL,
 
263
                                                  NULL);
 
264
        self->diagnostics_sig = sig;
 
265
 
 
266
        return self;
 
267
}
 
268
 
 
269
typedef struct {
 
270
        GDBusObjectManager *manager;
 
271
        GCancellable *cancellable;
 
272
        GList *realms;
 
273
} DiscoverClosure;
 
274
 
 
275
static void
 
276
discover_closure_free (gpointer data)
 
277
{
 
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);
 
283
}
 
284
 
 
285
static void
 
286
on_provider_discover (GObject *source,
 
287
                      GAsyncResult *result,
 
288
                      gpointer user_data)
 
289
{
 
290
        GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
 
291
        DiscoverClosure *discover = g_simple_async_result_get_op_res_gpointer (async);
 
292
        GDBusObject *object;
 
293
        GError *error = NULL;
 
294
        gchar **realms;
 
295
        gint relevance;
 
296
        gint i;
 
297
 
 
298
        um_realm_provider_call_discover_finish (UM_REALM_PROVIDER (source), &relevance,
 
299
                                                &realms, result, &error);
 
300
        if (error == NULL) {
 
301
                for (i = 0; realms[i]; i++) {
 
302
                        object = g_dbus_object_manager_get_object (discover->manager, realms[i]);
 
303
                        if (object == NULL)
 
304
                                g_warning ("Realm is not in object manager: %s", realms[i]);
 
305
                        else
 
306
                                discover->realms = g_list_prepend (discover->realms, object);
 
307
                }
 
308
 
 
309
        } else {
 
310
                g_simple_async_result_take_error (async, error);
 
311
                g_simple_async_result_complete (async);
 
312
        }
 
313
 
 
314
        g_object_unref (async);
 
315
}
 
316
 
 
317
void
 
318
um_realm_manager_discover (UmRealmManager *self,
 
319
                           const gchar *input,
 
320
                           GCancellable *cancellable,
 
321
                           GAsyncReadyCallback callback,
 
322
                           gpointer user_data)
 
323
{
 
324
        GSimpleAsyncResult *res;
 
325
        DiscoverClosure *discover;
 
326
        GVariant *options;
 
327
 
 
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));
 
331
 
 
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);
 
338
 
 
339
        options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
 
340
 
 
341
        um_realm_provider_call_discover (self->provider, input, options, cancellable,
 
342
                                         on_provider_discover, g_object_ref (res));
 
343
 
 
344
        g_object_unref (res);
 
345
}
 
346
 
 
347
GList *
 
348
um_realm_manager_discover_finish (UmRealmManager *self,
 
349
                                  GAsyncResult *result,
 
350
                                  GError **error)
 
351
{
 
352
        GSimpleAsyncResult *async;
 
353
        DiscoverClosure *discover;
 
354
        GList *realms;
 
355
 
 
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);
 
360
 
 
361
        async = G_SIMPLE_ASYNC_RESULT (result);
 
362
        if (g_simple_async_result_propagate_error (async, error))
 
363
                return NULL;
 
364
 
 
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"));
 
369
                return NULL;
 
370
        }
 
371
 
 
372
        realms = g_list_reverse (discover->realms);
 
373
        discover->realms = NULL;
 
374
        return realms;
 
375
}
 
376
 
 
377
GList *
 
378
um_realm_manager_get_realms (UmRealmManager *self)
 
379
{
 
380
        GList *objects;
 
381
        GList *realms = NULL;
 
382
        GList *l;
 
383
 
 
384
        g_return_val_if_fail (UM_IS_REALM_MANAGER (self), NULL);
 
385
 
 
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));
 
390
        }
 
391
 
 
392
        g_list_free_full (objects, g_object_unref);
 
393
        return realms;
 
394
}
 
395
 
 
396
static void
 
397
string_replace (GString *string,
 
398
                const gchar *find,
 
399
                const gchar *replace)
 
400
{
 
401
        const gchar *at;
 
402
        gssize pos;
 
403
 
 
404
        at = strstr (string->str, find);
 
405
        if (at != NULL) {
 
406
                pos = at - string->str;
 
407
                g_string_erase (string, pos, strlen (find));
 
408
                g_string_insert (string, pos, replace);
 
409
        }
 
410
}
 
411
 
 
412
gchar *
 
413
um_realm_calculate_login (UmRealmCommon *realm,
 
414
                          const gchar *username)
 
415
{
 
416
        GString *string;
 
417
        const gchar *const *formats;
 
418
        gchar *login = NULL;
 
419
 
 
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);
 
426
        }
 
427
 
 
428
        return login;
 
429
 
 
430
}
 
431
 
 
432
gboolean
 
433
um_realm_is_configured (UmRealmObject *realm)
 
434
{
 
435
        UmRealmCommon *common;
 
436
        const gchar *configured;
 
437
        gboolean is;
 
438
 
 
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);
 
443
 
 
444
        return is;
 
445
}
 
446
 
 
447
static const gchar *
 
448
find_supported_credentials (UmRealmKerberosMembership *membership,
 
449
                            const gchar *owner)
 
450
{
 
451
        const gchar *cred_owner;
 
452
        const gchar *cred_type;
 
453
        GVariant *supported;
 
454
        GVariantIter iter;
 
455
 
 
456
        supported = um_realm_kerberos_membership_get_supported_join_credentials (membership);
 
457
        g_return_val_if_fail (supported != NULL, NULL);
 
458
 
 
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);
 
465
                        }
 
466
                }
 
467
        }
 
468
 
 
469
        return NULL;
 
470
}
 
471
 
 
472
static void
 
473
on_realm_join_complete (GObject *source,
 
474
                        GAsyncResult *result,
 
475
                        gpointer user_data)
 
476
{
 
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);
 
481
}
 
482
 
 
483
static gboolean
 
484
realm_join_as_owner (UmRealmObject *realm,
 
485
                     const gchar *owner,
 
486
                     const gchar *login,
 
487
                     const gchar *password,
 
488
                     GBytes *credentials,
 
489
                     GCancellable *cancellable,
 
490
                     GAsyncReadyCallback callback,
 
491
                     gpointer user_data)
 
492
{
 
493
        UmRealmKerberosMembership *membership;
 
494
        GSimpleAsyncResult *async;
 
495
        GVariant *contents;
 
496
        GVariant *options;
 
497
        GVariant *creds;
 
498
        const gchar *type;
 
499
 
 
500
        membership = um_realm_object_get_kerberos_membership (realm);
 
501
        g_return_val_if_fail (membership != NULL, FALSE);
 
502
 
 
503
        type = find_supported_credentials (membership, owner);
 
504
        if (type == NULL) {
 
505
                g_object_unref (membership);
 
506
                return FALSE;
 
507
        }
 
508
 
 
509
        async = g_simple_async_result_new (G_OBJECT (realm), callback, user_data,
 
510
                                           realm_join_as_owner);
 
511
 
 
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);
 
517
 
 
518
        } else if (g_str_equal (type, "password")) {
 
519
                contents = g_variant_new ("(ss)", login, password);
 
520
 
 
521
        } else {
 
522
                g_assert_not_reached ();
 
523
        }
 
524
 
 
525
        creds = g_variant_new ("(ssv)", type, owner, contents);
 
526
        options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
 
527
 
 
528
        um_realm_kerberos_membership_call_join (membership, creds, options,
 
529
                                                cancellable, on_realm_join_complete,
 
530
                                                g_object_ref (async));
 
531
 
 
532
        g_object_unref (async);
 
533
        g_object_unref (membership);
 
534
 
 
535
        return TRUE;
 
536
}
 
537
 
 
538
gboolean
 
539
um_realm_join_as_user (UmRealmObject *realm,
 
540
                       const gchar *login,
 
541
                       const gchar *password,
 
542
                       GBytes *credentials,
 
543
                       GCancellable *cancellable,
 
544
                       GAsyncReadyCallback callback,
 
545
                       gpointer user_data)
 
546
{
 
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);
 
553
 
 
554
        return realm_join_as_owner (realm, "user", login, password,
 
555
                                    credentials, cancellable, callback, user_data);
 
556
}
 
557
 
 
558
gboolean
 
559
um_realm_join_as_admin (UmRealmObject *realm,
 
560
                        const gchar *login,
 
561
                        const gchar *password,
 
562
                        GBytes *credentials,
 
563
                        GCancellable *cancellable,
 
564
                        GAsyncReadyCallback callback,
 
565
                        gpointer user_data)
 
566
{
 
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);
 
573
 
 
574
        return realm_join_as_owner (realm, "administrator", login, password, credentials,
 
575
                                    cancellable, callback, user_data);
 
576
}
 
577
 
 
578
gboolean
 
579
um_realm_join_finish (UmRealmObject *realm,
 
580
                      GAsyncResult *result,
 
581
                      GError **error)
 
582
{
 
583
        UmRealmKerberosMembership *membership;
 
584
        GError *call_error = NULL;
 
585
        gchar *dbus_error;
 
586
        GAsyncResult *async;
 
587
 
 
588
        g_return_val_if_fail (UM_REALM_IS_OBJECT (realm), FALSE);
 
589
        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
590
 
 
591
        membership = um_realm_object_get_kerberos_membership (realm);
 
592
        g_return_val_if_fail (membership != NULL, FALSE);
 
593
 
 
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);
 
597
 
 
598
        if (call_error == NULL)
 
599
                return TRUE;
 
600
 
 
601
        dbus_error = g_dbus_error_get_remote_error (call_error);
 
602
        if (dbus_error == NULL) {
 
603
                g_propagate_error (error, call_error);
 
604
                return FALSE;
 
605
        }
 
606
 
 
607
        g_dbus_error_strip_remote_error (call_error);
 
608
 
 
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);
 
613
        } else {
 
614
                g_propagate_error (error, call_error);
 
615
        }
 
616
 
 
617
        g_free (dbus_error);
 
618
        return FALSE;
 
619
}
 
620
 
 
621
typedef struct {
 
622
        gchar *domain;
 
623
        gchar *realm;
 
624
        gchar *user;
 
625
        gchar *password;
 
626
        GBytes *credentials;
 
627
} LoginClosure;
 
628
 
 
629
static void
 
630
login_closure_free (gpointer data)
 
631
{
 
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);
 
639
}
 
640
 
 
641
static krb5_error_code
 
642
login_perform_kinit (krb5_context k5,
 
643
                     const gchar *realm,
 
644
                     const gchar *login,
 
645
                     const gchar *password,
 
646
                     const gchar *filename)
 
647
{
 
648
        krb5_get_init_creds_opt *opts;
 
649
        krb5_error_code code;
 
650
        krb5_principal principal;
 
651
        krb5_ccache ccache;
 
652
        krb5_creds creds;
 
653
        gchar *name;
 
654
 
 
655
        name = g_strdup_printf ("%s@%s", login, realm);
 
656
        code = krb5_parse_name (k5, name, &principal);
 
657
        g_free (name);
 
658
 
 
659
        if (code != 0)
 
660
                return code;
 
661
 
 
662
        if (filename == NULL)
 
663
                code = krb5_cc_default (k5, &ccache);
 
664
        else
 
665
                code = krb5_cc_resolve (k5, filename, &ccache);
 
666
 
 
667
        if (code != 0) {
 
668
                krb5_free_principal (k5, principal);
 
669
                return code;
 
670
        }
 
671
 
 
672
        code = krb5_get_init_creds_opt_alloc (k5, &opts);
 
673
        g_return_val_if_fail (code == 0, code);
 
674
 
 
675
        code = krb5_get_init_creds_opt_set_out_ccache (k5, opts, ccache);
 
676
        g_return_val_if_fail (code == 0, code);
 
677
 
 
678
        code = krb5_get_init_creds_password (k5, &creds, principal,
 
679
                                             (char *)password,
 
680
                                             NULL, 0, 0, NULL, opts);
 
681
 
 
682
        krb5_get_init_creds_opt_free (k5, opts);
 
683
        krb5_cc_close (k5, ccache);
 
684
        krb5_free_principal (k5, principal);
 
685
 
 
686
        if (code == 0)
 
687
                krb5_free_cred_contents (k5, &creds);
 
688
 
 
689
        return code;
 
690
}
 
691
 
 
692
static void
 
693
kinit_thread_func (GSimpleAsyncResult *async,
 
694
                   GObject *object,
 
695
                   GCancellable *cancellable)
 
696
{
 
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;
 
702
        gchar *contents;
 
703
        gsize length;
 
704
        gint temp_fd;
 
705
 
 
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);
 
709
        if (temp_fd == -1) {
 
710
                g_warning ("Couldn't create credential cache file: %s: %s",
 
711
                           filename, g_strerror (errno));
 
712
                g_free (filename);
 
713
                filename = NULL;
 
714
        } else {
 
715
                close (temp_fd);
 
716
        }
 
717
 
 
718
        code = krb5_init_context (&k5);
 
719
        if (code == 0) {
 
720
                code = login_perform_kinit (k5, login->realm, login->user,
 
721
                                            login->password, filename);
 
722
        }
 
723
 
 
724
        switch (code) {
 
725
        case 0:
 
726
                if (filename != NULL) {
 
727
                        g_file_get_contents (filename, &contents, &length, &error);
 
728
                        if (error == NULL) {
 
729
                                login->credentials = g_bytes_new_take (contents, length);
 
730
                        } else {
 
731
                                g_warning ("Couldn't read credential cache: %s", error->message);
 
732
                                g_error_free (error);
 
733
                        }
 
734
                }
 
735
                break;
 
736
 
 
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);
 
745
                break;
 
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"));
 
749
                break;
 
750
        default:
 
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));
 
754
                break;
 
755
        }
 
756
 
 
757
        if (filename) {
 
758
                g_unlink (filename);
 
759
                g_free (filename);
 
760
        }
 
761
 
 
762
        if (k5)
 
763
                krb5_free_context (k5);
 
764
}
 
765
 
 
766
void
 
767
um_realm_login (UmRealmObject *realm,
 
768
                const gchar *user,
 
769
                const gchar *password,
 
770
                GCancellable *cancellable,
 
771
                GAsyncReadyCallback callback,
 
772
                gpointer user_data)
 
773
{
 
774
        GSimpleAsyncResult *async;
 
775
        LoginClosure *login;
 
776
        UmRealmKerberos *kerberos;
 
777
 
 
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));
 
782
 
 
783
        kerberos = um_realm_object_get_kerberos (realm);
 
784
        g_return_if_fail (kerberos != NULL);
 
785
 
 
786
        async = g_simple_async_result_new (NULL, callback, user_data,
 
787
                                           um_realm_login);
 
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);
 
794
 
 
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);
 
798
 
 
799
        g_object_unref (async);
 
800
        g_object_unref (kerberos);
 
801
}
 
802
 
 
803
gboolean
 
804
um_realm_login_finish (GAsyncResult *result,
 
805
                       GBytes **credentials,
 
806
                       GError **error)
 
807
{
 
808
        GSimpleAsyncResult *async;
 
809
        LoginClosure *login;
 
810
 
 
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);
 
814
 
 
815
        async = G_SIMPLE_ASYNC_RESULT (result);
 
816
        if (g_simple_async_result_propagate_error (async, error))
 
817
                return FALSE;
 
818
 
 
819
        login = g_simple_async_result_get_op_res_gpointer (async);
 
820
        if (credentials) {
 
821
                if (login->credentials)
 
822
                        *credentials = g_bytes_ref (login->credentials);
 
823
                else
 
824
                        *credentials = NULL;
 
825
        }
 
826
 
 
827
        return TRUE;
 
828
}