GCC Code Coverage Report


Directory: ./
File: panels/system/users/cc-realm-manager.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 347 0.0%
Functions: 0 34 0.0%
Branches: 0 276 0.0%

Line Branch Exec Source
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
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 2 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, see <http://www.gnu.org/licenses/>.
17 *
18 * Written by: Stef Walter <stefw@gnome.org>
19 */
20
21 #include "config.h"
22
23 #include "cc-realm-manager.h"
24
25 #include <krb5/krb5.h>
26
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <glib/gstdio.h>
30
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <errno.h>
34 #include <fcntl.h>
35
36
37 struct _CcRealmManager {
38 CcRealmObjectManagerClient parent_instance;
39
40 CcRealmProvider *provider;
41 guint diagnostics_sig;
42 };
43
44 enum {
45 REALM_ADDED,
46 NUM_SIGNALS,
47 };
48
49 static gint signals[NUM_SIGNALS] = { 0, };
50
51 G_DEFINE_TYPE (CcRealmManager, cc_realm_manager, CC_REALM_TYPE_OBJECT_MANAGER_CLIENT);
52
53 GQuark
54 cc_realm_error_get_quark (void)
55 {
56 static GQuark quark = 0;
57 if (quark == 0)
58 quark = g_quark_from_static_string ("cc-realm-error");
59 return quark;
60 }
61
62 static gboolean
63 is_realm_with_kerberos_and_membership (gpointer object)
64 {
65 g_autoptr(GDBusInterface) kerberos_interface = NULL;
66 g_autoptr(GDBusInterface) kerberos_membership_interface = NULL;
67
68 if (!G_IS_DBUS_OBJECT (object))
69 return FALSE;
70
71 kerberos_interface = g_dbus_object_get_interface (object, "org.freedesktop.realmd.Kerberos");
72 if (kerberos_interface == NULL)
73 return FALSE;
74
75 kerberos_membership_interface = g_dbus_object_get_interface (object, "org.freedesktop.realmd.KerberosMembership");
76 if (kerberos_membership_interface == NULL)
77 return FALSE;
78
79 return TRUE;
80 }
81
82 static void
83 on_interface_added (CcRealmManager *self,
84 GDBusObject *object,
85 GDBusInterface *interface)
86 {
87 g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (interface), G_MAXINT);
88 }
89
90 static void
91 on_object_added (CcRealmManager *self,
92 GDBusObject *object)
93 {
94 GList *interfaces, *l;
95
96 interfaces = g_dbus_object_get_interfaces (object);
97 for (l = interfaces; l != NULL; l = g_list_next (l))
98 on_interface_added (self, object, l->data);
99 g_list_free_full (interfaces, g_object_unref);
100
101 if (is_realm_with_kerberos_and_membership (object)) {
102 g_debug ("Saw realm: %s", g_dbus_object_get_object_path (object));
103 g_signal_emit (self, signals[REALM_ADDED], 0, object);
104 }
105 }
106
107 static void
108 cc_realm_manager_init (CcRealmManager *self)
109 {
110 g_signal_connect (self, "object-added", G_CALLBACK (on_object_added), NULL);
111 g_signal_connect (self, "interface-added", G_CALLBACK (on_interface_added), NULL);
112 }
113
114 static void
115 cc_realm_manager_dispose (GObject *obj)
116 {
117 CcRealmManager *self = CC_REALM_MANAGER (obj);
118 GDBusConnection *connection;
119
120 g_clear_object (&self->provider);
121
122 if (self->diagnostics_sig) {
123 connection = g_dbus_object_manager_client_get_connection (G_DBUS_OBJECT_MANAGER_CLIENT (self));
124 if (connection != NULL)
125 g_dbus_connection_signal_unsubscribe (connection, self->diagnostics_sig);
126 self->diagnostics_sig = 0;
127 }
128
129 G_OBJECT_CLASS (cc_realm_manager_parent_class)->dispose (obj);
130 }
131
132 static void
133 cc_realm_manager_class_init (CcRealmManagerClass *klass)
134 {
135 GObjectClass *object_class = G_OBJECT_CLASS (klass);
136
137 object_class->dispose = cc_realm_manager_dispose;
138
139 signals[REALM_ADDED] = g_signal_new ("realm-added", CC_TYPE_REALM_MANAGER,
140 G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
141 g_cclosure_marshal_generic,
142 G_TYPE_NONE, 1, CC_REALM_TYPE_OBJECT);
143 }
144
145 static void
146 on_realm_diagnostics (GDBusConnection *connection,
147 const gchar *sender_name,
148 const gchar *object_path,
149 const gchar *interface_name,
150 const gchar *signal_name,
151 GVariant *parameters,
152 gpointer user_data)
153 {
154 const gchar *message;
155 const gchar *unused;
156
157 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(ss)"))) {
158 /* Data is already formatted appropriately for stderr */
159 g_variant_get (parameters, "(&s&s)", &message, &unused);
160 g_printerr ("%s", message);
161 }
162 }
163
164 static void
165 on_provider_new (GObject *source,
166 GAsyncResult *result,
167 gpointer user_data)
168 {
169 g_autoptr(GTask) task = G_TASK (user_data);
170 CcRealmManager *manager = g_task_get_task_data (task);
171 GError *error = NULL;
172
173 manager->provider = cc_realm_provider_proxy_new_finish (result, &error);
174 if (error == NULL) {
175 g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (manager->provider), -1);
176 g_debug ("Created realm manager");
177 g_task_return_pointer (task, g_object_ref (manager), g_object_unref);
178 } else {
179 g_task_return_error (task, error);
180 }
181 }
182
183 static void
184 on_manager_new (GObject *source,
185 GAsyncResult *result,
186 gpointer user_data)
187 {
188 g_autoptr(GTask) task = G_TASK (user_data);
189 CcRealmManager *manager;
190 GDBusConnection *connection;
191 GError *error = NULL;
192 GObject *object;
193 guint sig;
194
195 object = g_async_initable_new_finish (G_ASYNC_INITABLE (source), result, &error);
196 if (error == NULL) {
197 manager = CC_REALM_MANAGER (object);
198 connection = g_dbus_object_manager_client_get_connection (G_DBUS_OBJECT_MANAGER_CLIENT (object));
199
200 g_debug ("Connected to realmd");
201
202 sig = g_dbus_connection_signal_subscribe (connection,
203 "org.freedesktop.realmd",
204 "org.freedesktop.realmd.Service",
205 "Diagnostics",
206 NULL,
207 NULL,
208 G_DBUS_SIGNAL_FLAGS_NONE,
209 on_realm_diagnostics,
210 NULL,
211 NULL);
212 manager->diagnostics_sig = sig;
213
214 g_task_set_task_data (task, manager, g_object_unref);
215
216 cc_realm_provider_proxy_new (connection,
217 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
218 "org.freedesktop.realmd",
219 "/org/freedesktop/realmd",
220 g_task_get_cancellable (task),
221 on_provider_new, task);
222 g_steal_pointer (&task);
223 } else {
224 g_task_return_error (task, error);
225 }
226 }
227
228 void
229 cc_realm_manager_new (GCancellable *cancellable,
230 GAsyncReadyCallback callback,
231 gpointer user_data)
232 {
233 GTask *task;
234
235 g_debug ("Connecting to realmd...");
236
237 task = g_task_new (NULL, cancellable, callback, user_data);
238 g_task_set_source_tag (task, cc_realm_manager_new);
239
240 g_async_initable_new_async (CC_TYPE_REALM_MANAGER, G_PRIORITY_DEFAULT,
241 cancellable, on_manager_new, task,
242 "flags", G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
243 "name", "org.freedesktop.realmd",
244 "bus-type", G_BUS_TYPE_SYSTEM,
245 "object-path", "/org/freedesktop/realmd",
246 "get-proxy-type-func", cc_realm_object_manager_client_get_proxy_type,
247 NULL);
248 }
249
250 CcRealmManager *
251 cc_realm_manager_new_finish (GAsyncResult *result,
252 GError **error)
253 {
254 g_return_val_if_fail (g_task_is_valid (result, NULL), NULL);
255 g_return_val_if_fail (g_async_result_is_tagged (result, cc_realm_manager_new), NULL);
256
257 return g_task_propagate_pointer (G_TASK (result), error);
258 }
259
260 static void
261 realms_free (gpointer data)
262 {
263 g_list_free_full (data, g_object_unref);
264 }
265
266 static void
267 on_provider_discover (GObject *source,
268 GAsyncResult *result,
269 gpointer user_data)
270 {
271 g_autoptr(GTask) task = G_TASK (user_data);
272 CcRealmManager *manager = g_task_get_source_object (task);
273 GError *error = NULL;
274 gboolean no_membership = FALSE;
275 gchar **realms;
276 gint relevance;
277 gint i;
278 GList *kerberos_realms = NULL;
279
280 cc_realm_provider_call_discover_finish (CC_REALM_PROVIDER (source), &relevance,
281 &realms, result, &error);
282 if (error == NULL) {
283 for (i = 0; realms[i]; i++) {
284 g_autoptr(GDBusObject) object = NULL;
285
286 object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (manager), realms[i]);
287 if (object == NULL) {
288 g_warning ("Realm is not in object manager: %s", realms[i]);
289 } else {
290 if (is_realm_with_kerberos_and_membership (object)) {
291 g_debug ("Discovered realm: %s", realms[i]);
292 kerberos_realms = g_list_prepend (kerberos_realms, g_steal_pointer (&object));
293 } else {
294 g_debug ("Realm does not support kerberos membership: %s", realms[i]);
295 no_membership = TRUE;
296 }
297 }
298 }
299 g_strfreev (realms);
300
301 if (!kerberos_realms && no_membership) {
302 g_task_return_new_error (task, CC_REALM_ERROR, CC_REALM_ERROR_GENERIC,
303 _("Cannot automatically join this type of domain"));
304 } else if (!kerberos_realms) {
305 g_task_return_new_error (task, CC_REALM_ERROR, CC_REALM_ERROR_GENERIC,
306 _("No such domain or realm found"));
307 } else {
308 kerberos_realms = g_list_reverse (kerberos_realms);
309 g_task_return_pointer (task, kerberos_realms, realms_free);
310 }
311 } else {
312 g_task_return_error (task, error);
313 }
314 }
315
316 void
317 cc_realm_manager_discover (CcRealmManager *self,
318 const gchar *input,
319 GCancellable *cancellable,
320 GAsyncReadyCallback callback,
321 gpointer user_data)
322 {
323 GTask *task;
324 GVariant *options;
325
326 g_return_if_fail (CC_IS_REALM_MANAGER (self));
327 g_return_if_fail (input != NULL);
328 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
329
330 g_debug ("Discovering realms for: %s", input);
331
332 task = g_task_new (G_OBJECT (self), cancellable, callback, user_data);
333 g_task_set_source_tag (task, cc_realm_manager_discover);
334
335 options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
336
337 cc_realm_provider_call_discover (self->provider, input, options, cancellable,
338 on_provider_discover, task);
339 }
340
341 GList *
342 cc_realm_manager_discover_finish (CcRealmManager *self,
343 GAsyncResult *result,
344 GError **error)
345 {
346 g_return_val_if_fail (CC_IS_REALM_MANAGER (self), NULL);
347 g_return_val_if_fail (g_task_is_valid (result, G_OBJECT (self)), NULL);
348 g_return_val_if_fail (g_async_result_is_tagged (result, cc_realm_manager_discover), NULL);
349 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
350
351 return g_task_propagate_pointer (G_TASK (result), error);
352 }
353
354 GList *
355 cc_realm_manager_get_realms (CcRealmManager *self)
356 {
357 GList *objects;
358 GList *realms = NULL;
359 GList *l;
360
361 g_return_val_if_fail (CC_IS_REALM_MANAGER (self), NULL);
362
363 objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self));
364 for (l = objects; l != NULL; l = g_list_next (l)) {
365 if (is_realm_with_kerberos_and_membership (l->data))
366 realms = g_list_prepend (realms, g_object_ref (l->data));
367 }
368
369 g_list_free_full (objects, g_object_unref);
370 return realms;
371 }
372
373 static void
374 string_replace (GString *string,
375 const gchar *find,
376 const gchar *replace)
377 {
378 const gchar *at;
379 gssize pos;
380
381 at = strstr (string->str, find);
382 if (at != NULL) {
383 pos = at - string->str;
384 g_string_erase (string, pos, strlen (find));
385 g_string_insert (string, pos, replace);
386 }
387 }
388
389 gchar *
390 cc_realm_calculate_login (CcRealmCommon *realm,
391 const gchar *username)
392 {
393 const gchar *const *formats;
394
395 formats = cc_realm_common_get_login_formats (realm);
396 if (formats[0] != NULL) {
397 GString *string = g_string_new (formats[0]);
398 string_replace (string, "%U", username);
399 string_replace (string, "%D", cc_realm_common_get_name (realm));
400 return g_string_free (string, FALSE);
401 }
402
403 return NULL;
404 }
405
406 gboolean
407 cc_realm_is_configured (CcRealmObject *realm)
408 {
409 g_autoptr(CcRealmCommon) common = NULL;
410 const gchar *configured;
411 gboolean is = FALSE;
412
413 common = cc_realm_object_get_common (realm);
414 if (common != NULL) {
415 configured = cc_realm_common_get_configured (common);
416 is = configured != NULL && !g_str_equal (configured, "");
417 }
418
419 return is;
420 }
421
422 static const gchar *
423 find_supported_credentials (CcRealmKerberosMembership *membership,
424 const gchar *owner)
425 {
426 const gchar *cred_owner;
427 const gchar *cred_type;
428 GVariant *supported;
429 GVariantIter iter;
430
431 supported = cc_realm_kerberos_membership_get_supported_join_credentials (membership);
432 g_return_val_if_fail (supported != NULL, NULL);
433
434 g_variant_iter_init (&iter, supported);
435 while (g_variant_iter_loop (&iter, "(&s&s)", &cred_type, &cred_owner)) {
436 if (g_str_equal (owner, cred_owner)) {
437 if (g_str_equal (cred_type, "ccache") ||
438 g_str_equal (cred_type, "password")) {
439 return g_intern_string (cred_type);
440 }
441 }
442 }
443
444 return NULL;
445 }
446
447 static void
448 join_cb (GObject *source,
449 GAsyncResult *result,
450 gpointer user_data)
451 {
452 g_autoptr(GTask) task = G_TASK (user_data);
453 g_autoptr(GError) call_error = NULL;
454 g_autofree gchar *dbus_error = NULL;
455
456 if (cc_realm_kerberos_membership_call_join_finish (CC_REALM_KERBEROS_MEMBERSHIP (source), result, &call_error)) {
457 g_debug ("Completed Join() method call");
458
459 g_task_return_boolean (task, TRUE);
460 return;
461 }
462
463 dbus_error = g_dbus_error_get_remote_error (call_error);
464 if (dbus_error == NULL) {
465 g_debug ("Join() failed because of %s", call_error->message);
466 g_task_return_error (task, g_steal_pointer (&call_error));
467 return;
468 }
469
470 g_dbus_error_strip_remote_error (call_error);
471
472 if (g_str_equal (dbus_error, "org.freedesktop.realmd.Error.AuthenticationFailed")) {
473 g_debug ("Join() failed because of invalid/insufficient credentials");
474 g_task_return_new_error (task, CC_REALM_ERROR, CC_REALM_ERROR_BAD_LOGIN, "%s", call_error->message);
475 } else if (g_str_equal (dbus_error, "org.freedesktop.realmd.Error.BadHostname")) {
476 g_debug ("Join() failed because of invalid/conflicting host name");
477 g_task_return_new_error (task, CC_REALM_ERROR, CC_REALM_ERROR_BAD_HOSTNAME, "%s", call_error->message);
478 } else {
479 g_debug ("Join() failed because of %s", call_error->message);
480 g_task_return_error (task, g_steal_pointer (&call_error));
481 }
482 }
483
484 static gboolean
485 realm_join_as_owner (CcRealmObject *realm,
486 const gchar *owner,
487 const gchar *login,
488 const gchar *password,
489 GBytes *credentials,
490 GCancellable *cancellable,
491 GAsyncReadyCallback callback,
492 gpointer user_data)
493 {
494 g_autoptr(CcRealmKerberosMembership) membership = NULL;
495 GVariant *contents;
496 GVariant *options;
497 GVariant *option;
498 GVariant *creds;
499 const gchar *type;
500 GTask *task;
501
502 membership = cc_realm_object_get_kerberos_membership (realm);
503 g_return_val_if_fail (membership != NULL, FALSE);
504
505 type = find_supported_credentials (membership, owner);
506 if (type == NULL) {
507 g_debug ("Couldn't find supported credential type for owner: %s", owner);
508 return FALSE;
509 }
510
511 if (g_str_equal (type, "ccache")) {
512 g_debug ("Using a kerberos credential cache to join the realm");
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 g_debug ("Using a user/password to join the realm");
520 contents = g_variant_new ("(ss)", login, password);
521
522 } else {
523 g_assert_not_reached ();
524 }
525
526 creds = g_variant_new ("(ssv)", type, owner, contents);
527 option = g_variant_new ("{sv}", "manage-system", g_variant_new_boolean (FALSE));
528 options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), &option, 1);
529
530 g_debug ("Calling the Join() method with %s credentials", owner);
531
532 task = g_task_new (realm, cancellable, callback, user_data);
533 cc_realm_kerberos_membership_call_join (membership, creds, options,
534 cancellable, join_cb, task);
535
536 return TRUE;
537 }
538
539 gboolean
540 cc_realm_join_as_user (CcRealmObject *realm,
541 const gchar *login,
542 const gchar *password,
543 GBytes *credentials,
544 GCancellable *cancellable,
545 GAsyncReadyCallback callback,
546 gpointer user_data)
547 {
548 g_return_val_if_fail (CC_REALM_IS_OBJECT (realm), FALSE);
549 g_return_val_if_fail (credentials != NULL, FALSE);
550 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
551 g_return_val_if_fail (login != NULL, FALSE);
552 g_return_val_if_fail (password != NULL, FALSE);
553 g_return_val_if_fail (credentials != NULL, FALSE);
554
555 return realm_join_as_owner (realm, "user", login, password,
556 credentials, cancellable, callback, user_data);
557 }
558
559 gboolean
560 cc_realm_join_as_admin (CcRealmObject *realm,
561 const gchar *login,
562 const gchar *password,
563 GBytes *credentials,
564 GCancellable *cancellable,
565 GAsyncReadyCallback callback,
566 gpointer user_data)
567 {
568 g_return_val_if_fail (CC_REALM_IS_OBJECT (realm), FALSE);
569 g_return_val_if_fail (credentials != NULL, FALSE);
570 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
571 g_return_val_if_fail (login != NULL, FALSE);
572 g_return_val_if_fail (password != NULL, FALSE);
573 g_return_val_if_fail (credentials != NULL, FALSE);
574
575 return realm_join_as_owner (realm, "administrator", login, password, credentials,
576 cancellable, callback, user_data);
577 }
578
579 gboolean
580 cc_realm_join_finish (CcRealmObject *realm,
581 GAsyncResult *result,
582 GError **error)
583 {
584 g_return_val_if_fail (CC_REALM_IS_OBJECT (realm), FALSE);
585 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
586
587 return g_task_propagate_boolean (G_TASK (result), error);
588 }
589
590 typedef struct {
591 gchar *domain;
592 gchar *realm;
593 gchar *user;
594 gchar *password;
595 } LoginClosure;
596
597 static void
598 login_closure_free (gpointer data)
599 {
600 LoginClosure *login = data;
601 g_clear_pointer (&login->domain, g_free);
602 g_clear_pointer (&login->realm, g_free);
603 g_clear_pointer (&login->user, g_free);
604 g_clear_pointer (&login->password, g_free);
605 g_slice_free (LoginClosure, login);
606 }
607
608 static krb5_error_code
609 login_perform_kinit (krb5_context k5,
610 const gchar *realm,
611 const gchar *login,
612 const gchar *password,
613 const gchar *filename)
614 {
615 krb5_get_init_creds_opt *opts;
616 krb5_error_code code;
617 krb5_principal principal;
618 krb5_ccache ccache;
619 krb5_creds creds;
620 g_autofree gchar *name = NULL;
621
622 name = g_strdup_printf ("%s@%s", login, realm);
623 code = krb5_parse_name (k5, name, &principal);
624
625 if (code != 0) {
626 g_debug ("Couldn't parse principal name: %s: %s",
627 name, krb5_get_error_message (k5, code));
628 return code;
629 }
630
631 g_debug ("Using principal name to kinit: %s", name);
632
633 if (filename == NULL)
634 code = krb5_cc_default (k5, &ccache);
635 else
636 code = krb5_cc_resolve (k5, filename, &ccache);
637
638 if (code != 0) {
639 krb5_free_principal (k5, principal);
640 g_debug ("Couldn't open credential cache: %s: %s",
641 filename ? filename : "<default>",
642 krb5_get_error_message (k5, code));
643 return code;
644 }
645
646 code = krb5_get_init_creds_opt_alloc (k5, &opts);
647 g_return_val_if_fail (code == 0, code);
648
649 code = krb5_get_init_creds_opt_set_out_ccache (k5, opts, ccache);
650 g_return_val_if_fail (code == 0, code);
651
652 code = krb5_get_init_creds_password (k5, &creds, principal,
653 (char *)password,
654 NULL, 0, 0, NULL, opts);
655
656 krb5_get_init_creds_opt_free (k5, opts);
657 krb5_cc_close (k5, ccache);
658 krb5_free_principal (k5, principal);
659
660 if (code == 0) {
661 g_debug ("kinit succeeded");
662 krb5_free_cred_contents (k5, &creds);
663 } else {
664 g_debug ("kinit failed: %s", krb5_get_error_message (k5, code));
665 }
666
667 return code;
668 }
669
670 static void
671 kinit_thread_func (GTask *t,
672 gpointer object,
673 gpointer task_data,
674 GCancellable *cancellable)
675 {
676 g_autoptr(GTask) task = t;
677 LoginClosure *login = task_data;
678 krb5_context k5 = NULL;
679 krb5_error_code code;
680 g_autofree gchar *filename = NULL;
681 gchar *contents;
682 gsize length;
683 gint temp_fd;
684
685 filename = g_build_filename (g_get_user_runtime_dir (),
686 "um-krb5-creds.XXXXXX", NULL);
687 temp_fd = g_mkstemp_full (filename, O_RDWR, S_IRUSR | S_IWUSR);
688 if (temp_fd == -1) {
689 g_warning ("Couldn't create credential cache file: %s: %s",
690 filename, g_strerror (errno));
691 g_clear_pointer (&filename, g_free);
692 } else {
693 close (temp_fd);
694 }
695
696 code = krb5_init_context (&k5);
697 if (code == 0) {
698 code = login_perform_kinit (k5, login->realm, login->user,
699 login->password, filename);
700 }
701
702 switch (code) {
703 case 0:
704 if (filename != NULL) {
705 g_autoptr(GError) error = NULL;
706
707 if (g_file_get_contents (filename, &contents, &length, &error)) {
708 g_debug ("Read in credential cache: %s", filename);
709 } else {
710 g_warning ("Couldn't read credential cache: %s: %s",
711 filename, error->message);
712 }
713
714 g_task_return_pointer (task, g_bytes_new_take (contents, length), (GDestroyNotify) g_bytes_unref);
715 }
716 break;
717
718 case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
719 case KRB5KDC_ERR_POLICY:
720 g_task_return_new_error (task, CC_REALM_ERROR, CC_REALM_ERROR_BAD_LOGIN,
721 _("Cannot log in as %s at the %s domain"),
722 login->user, login->domain);
723 break;
724 case KRB5KDC_ERR_PREAUTH_FAILED:
725 case KRB5KRB_AP_ERR_BAD_INTEGRITY:
726 g_task_return_new_error (task, CC_REALM_ERROR, CC_REALM_ERROR_BAD_PASSWORD,
727 _("Invalid password, please try again"));
728 break;
729 case KRB5_PREAUTH_FAILED:
730 case KRB5KDC_ERR_KEY_EXP:
731 case KRB5KDC_ERR_CLIENT_REVOKED:
732 case KRB5KDC_ERR_ETYPE_NOSUPP:
733 case KRB5_PROG_ETYPE_NOSUPP:
734 g_task_return_new_error (task, CC_REALM_ERROR, CC_REALM_ERROR_CANNOT_AUTH,
735 _("Cannot log in as %s at the %s domain"),
736 login->user, login->domain);
737 break;
738 default:
739 g_task_return_new_error (task, CC_REALM_ERROR, CC_REALM_ERROR_GENERIC,
740 _("Couldn’t connect to the %s domain: %s"),
741 login->domain, krb5_get_error_message (k5, code));
742 break;
743 }
744
745 if (filename) {
746 g_unlink (filename);
747 g_debug ("Deleted credential cache: %s", filename);
748 }
749
750 if (k5)
751 krb5_free_context (k5);
752 }
753
754 void
755 cc_realm_login (CcRealmObject *realm,
756 const gchar *user,
757 const gchar *password,
758 GCancellable *cancellable,
759 GAsyncReadyCallback callback,
760 gpointer user_data)
761 {
762 GTask *task;
763 LoginClosure *login;
764 g_autoptr(CcRealmKerberos) kerberos = NULL;
765
766 g_return_if_fail (CC_REALM_IS_OBJECT (realm));
767 g_return_if_fail (user != NULL);
768 g_return_if_fail (password != NULL);
769 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
770
771 kerberos = cc_realm_object_get_kerberos (realm);
772 g_return_if_fail (kerberos != NULL);
773
774 task = g_task_new (NULL, cancellable, callback, user_data);
775 g_task_set_source_tag (task, cc_realm_login);
776
777 login = g_slice_new0 (LoginClosure);
778 login->domain = g_strdup (cc_realm_kerberos_get_domain_name (kerberos));
779 login->realm = g_strdup (cc_realm_kerberos_get_realm_name (kerberos));
780 login->user = g_strdup (user);
781 login->password = g_strdup (password);
782 g_task_set_task_data (task, login, login_closure_free);
783
784 g_task_set_return_on_cancel (task, TRUE);
785 g_task_run_in_thread (task, kinit_thread_func);
786 }
787
788 GBytes *
789 cc_realm_login_finish (GAsyncResult *result,
790 GError **error)
791 {
792 g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
793 g_return_val_if_fail (g_async_result_is_tagged (result, cc_realm_login), FALSE);
794 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
795
796 return g_task_propagate_pointer (G_TASK (result), error);
797 }
798