The version property may not be available if realmd is version 0.1 (which lacked the property) or if other properties changed signatures (causing gdbus to fail to load cache any property) This commit fixes a crash where property is NULL in those cases.
828 lines
28 KiB
C
828 lines
28 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
|
*
|
|
* Copyright 2009-2012 Red Hat, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* Written by: Matthias Clasen <mclasen@redhat.com>
|
|
* Stef Walter <stefw@gnome.org>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "um-realm-manager.h"
|
|
|
|
#include <krb5/krb5.h>
|
|
|
|
#include <glib.h>
|
|
#include <glib/gi18n.h>
|
|
#include <glib/gstdio.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
|
|
|
|
struct _UmRealmManager {
|
|
UmRealmObjectManagerClient parent;
|
|
UmRealmProvider *provider;
|
|
guint diagnostics_sig;
|
|
};
|
|
|
|
typedef struct {
|
|
UmRealmProviderProxyClass parent_class;
|
|
} UmRealmManagerClass;
|
|
|
|
enum {
|
|
REALM_ADDED,
|
|
NUM_SIGNALS,
|
|
};
|
|
|
|
static gint signals[NUM_SIGNALS] = { 0, };
|
|
|
|
G_DEFINE_TYPE (UmRealmManager, um_realm_manager, UM_REALM_TYPE_OBJECT_MANAGER_CLIENT);
|
|
|
|
GQuark
|
|
um_realm_error_get_quark (void)
|
|
{
|
|
static GQuark quark = 0;
|
|
if (quark == 0)
|
|
quark = g_quark_from_static_string ("um-realm-error");
|
|
return quark;
|
|
}
|
|
|
|
static gboolean
|
|
is_realm_with_kerberos_and_membership (gpointer object)
|
|
{
|
|
GDBusInterface *interface;
|
|
|
|
if (!G_IS_DBUS_OBJECT (object))
|
|
return FALSE;
|
|
|
|
interface = g_dbus_object_get_interface (object, "org.freedesktop.realmd.Kerberos");
|
|
if (interface == NULL)
|
|
return FALSE;
|
|
g_object_unref (interface);
|
|
|
|
interface = g_dbus_object_get_interface (object, "org.freedesktop.realmd.KerberosMembership");
|
|
if (interface == NULL)
|
|
return FALSE;
|
|
g_object_unref (interface);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
on_interface_added (GDBusObjectManager *manager,
|
|
GDBusObject *object,
|
|
GDBusInterface *interface)
|
|
{
|
|
g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (interface), G_MAXINT);
|
|
}
|
|
|
|
static void
|
|
on_object_added (GDBusObjectManager *manager,
|
|
GDBusObject *object,
|
|
gpointer user_data)
|
|
{
|
|
if (is_realm_with_kerberos_and_membership (object))
|
|
g_signal_emit (user_data, signals[REALM_ADDED], 0, object);
|
|
}
|
|
|
|
static void
|
|
um_realm_manager_init (UmRealmManager *self)
|
|
{
|
|
g_signal_connect (self, "object-added", G_CALLBACK (on_object_added), self);
|
|
g_signal_connect (self, "interface-added", G_CALLBACK (on_interface_added), self);
|
|
}
|
|
|
|
static void
|
|
um_realm_manager_dispose (GObject *obj)
|
|
{
|
|
UmRealmManager *self = UM_REALM_MANAGER (obj);
|
|
GDBusConnection *connection;
|
|
|
|
g_clear_object (&self->provider);
|
|
|
|
if (self->diagnostics_sig) {
|
|
connection = g_dbus_object_manager_client_get_connection (G_DBUS_OBJECT_MANAGER_CLIENT (self));
|
|
if (connection != NULL)
|
|
g_dbus_connection_signal_unsubscribe (connection, self->diagnostics_sig);
|
|
self->diagnostics_sig = 0;
|
|
}
|
|
|
|
G_OBJECT_CLASS (um_realm_manager_parent_class)->dispose (obj);
|
|
}
|
|
|
|
static void
|
|
um_realm_manager_class_init (UmRealmManagerClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = um_realm_manager_dispose;
|
|
|
|
signals[REALM_ADDED] = g_signal_new ("realm-added", UM_TYPE_REALM_MANAGER,
|
|
G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
|
|
g_cclosure_marshal_generic,
|
|
G_TYPE_NONE, 1, UM_REALM_TYPE_OBJECT);
|
|
}
|
|
|
|
static void
|
|
on_realm_diagnostics (GDBusConnection *connection,
|
|
const gchar *sender_name,
|
|
const gchar *object_path,
|
|
const gchar *interface_name,
|
|
const gchar *signal_name,
|
|
GVariant *parameters,
|
|
gpointer user_data)
|
|
{
|
|
const gchar *message;
|
|
const gchar *unused;
|
|
|
|
if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(ss)"))) {
|
|
/* Data is already formatted appropriately for stderr */
|
|
g_variant_get (parameters, "(&s&s)", &message, &unused);
|
|
g_printerr ("%s", message);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
number_at_least (const gchar *number,
|
|
guint minimum)
|
|
{
|
|
gchar *end;
|
|
|
|
if (strtol (number, &end, 10) < (long)minimum)
|
|
return FALSE;
|
|
if (!end || *end != '\0')
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
version_compare (const char *version,
|
|
guint req_major,
|
|
guint req_minor)
|
|
{
|
|
gboolean match = FALSE;
|
|
gchar **parts;
|
|
|
|
parts = g_strsplit (version, ".", 2);
|
|
|
|
if (parts[0] && parts[1]) {
|
|
match = number_at_least (parts[0], req_major) &&
|
|
number_at_least (parts[1], req_minor);
|
|
}
|
|
|
|
g_strfreev (parts);
|
|
return match;
|
|
}
|
|
|
|
void
|
|
um_realm_manager_new (GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
g_async_initable_new_async (UM_TYPE_REALM_MANAGER, G_PRIORITY_DEFAULT,
|
|
cancellable, callback, user_data,
|
|
"flags", G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
|
|
"name", "org.freedesktop.realmd",
|
|
"bus-type", G_BUS_TYPE_SYSTEM,
|
|
"object-path", "/org/freedesktop/realmd",
|
|
"get-proxy-type-func", um_realm_object_manager_client_get_proxy_type,
|
|
NULL);
|
|
}
|
|
|
|
UmRealmManager *
|
|
um_realm_manager_new_finish (GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
UmRealmManager *self;
|
|
GDBusConnection *connection;
|
|
GObject *source_object;
|
|
const gchar *version;
|
|
GObject *ret;
|
|
guint sig;
|
|
|
|
source_object = g_async_result_get_source_object (result);
|
|
ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, error);
|
|
g_object_unref (source_object);
|
|
|
|
if (ret == NULL)
|
|
return NULL;
|
|
|
|
self = UM_REALM_MANAGER (ret);
|
|
connection = g_dbus_object_manager_client_get_connection (G_DBUS_OBJECT_MANAGER_CLIENT (self));
|
|
|
|
/*
|
|
* TODO: Remove this version checking. This is temporary code, so
|
|
* just use sync here. Shortly we'll be stabilizing the realmd
|
|
* interfaces.
|
|
*/
|
|
|
|
self->provider = um_realm_provider_proxy_new_sync (connection, G_DBUS_PROXY_FLAGS_NONE,
|
|
"org.freedesktop.realmd",
|
|
"/org/freedesktop/realmd",
|
|
NULL, error);
|
|
if (self->provider == NULL) {
|
|
g_object_unref (self);
|
|
return NULL;
|
|
}
|
|
|
|
version = um_realm_provider_get_version (self->provider);
|
|
if (version == NULL || !version_compare (version, 0, 7)) {
|
|
/* No need to bother translators with this temporary message */
|
|
g_set_error (error, UM_REALM_ERROR, UM_REALM_ERROR_GENERIC,
|
|
"realmd version should be at least 0.7");
|
|
g_object_unref (self);
|
|
return NULL;
|
|
}
|
|
|
|
sig = g_dbus_connection_signal_subscribe (connection,
|
|
"org.freedesktop.realmd",
|
|
"org.freedesktop.realmd.Service",
|
|
"Diagnostics",
|
|
NULL,
|
|
NULL,
|
|
G_DBUS_SIGNAL_FLAGS_NONE,
|
|
on_realm_diagnostics,
|
|
NULL,
|
|
NULL);
|
|
self->diagnostics_sig = sig;
|
|
|
|
return self;
|
|
}
|
|
|
|
typedef struct {
|
|
GDBusObjectManager *manager;
|
|
GCancellable *cancellable;
|
|
GList *realms;
|
|
} DiscoverClosure;
|
|
|
|
static void
|
|
discover_closure_free (gpointer data)
|
|
{
|
|
DiscoverClosure *discover = data;
|
|
g_object_unref (discover->manager);
|
|
g_clear_object (&discover->cancellable);
|
|
g_list_free_full (discover->realms, g_object_unref);
|
|
g_slice_free (DiscoverClosure, discover);
|
|
}
|
|
|
|
static void
|
|
on_provider_discover (GObject *source,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
|
|
DiscoverClosure *discover = g_simple_async_result_get_op_res_gpointer (async);
|
|
GDBusObject *object;
|
|
GError *error = NULL;
|
|
gchar **realms;
|
|
gint relevance;
|
|
gint i;
|
|
|
|
um_realm_provider_call_discover_finish (UM_REALM_PROVIDER (source), &relevance,
|
|
&realms, result, &error);
|
|
if (error == NULL) {
|
|
for (i = 0; realms[i]; i++) {
|
|
object = g_dbus_object_manager_get_object (discover->manager, realms[i]);
|
|
if (object == NULL)
|
|
g_warning ("Realm is not in object manager: %s", realms[i]);
|
|
else
|
|
discover->realms = g_list_prepend (discover->realms, object);
|
|
}
|
|
|
|
} else {
|
|
g_simple_async_result_take_error (async, error);
|
|
g_simple_async_result_complete (async);
|
|
}
|
|
|
|
g_object_unref (async);
|
|
}
|
|
|
|
void
|
|
um_realm_manager_discover (UmRealmManager *self,
|
|
const gchar *input,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *res;
|
|
DiscoverClosure *discover;
|
|
GVariant *options;
|
|
|
|
g_return_if_fail (UM_IS_REALM_MANAGER (self));
|
|
g_return_if_fail (input != NULL);
|
|
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
|
|
|
|
res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
|
|
um_realm_manager_discover);
|
|
discover = g_slice_new0 (DiscoverClosure);
|
|
discover->manager = g_object_ref (self);
|
|
discover->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
|
|
g_simple_async_result_set_op_res_gpointer (res, discover, discover_closure_free);
|
|
|
|
options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
|
|
|
|
um_realm_provider_call_discover (self->provider, input, options, cancellable,
|
|
on_provider_discover, g_object_ref (res));
|
|
|
|
g_object_unref (res);
|
|
}
|
|
|
|
GList *
|
|
um_realm_manager_discover_finish (UmRealmManager *self,
|
|
GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
GSimpleAsyncResult *async;
|
|
DiscoverClosure *discover;
|
|
GList *realms;
|
|
|
|
g_return_val_if_fail (UM_IS_REALM_MANAGER (self), NULL);
|
|
g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
|
|
um_realm_manager_discover), NULL);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
|
|
|
async = G_SIMPLE_ASYNC_RESULT (result);
|
|
if (g_simple_async_result_propagate_error (async, error))
|
|
return NULL;
|
|
|
|
discover = g_simple_async_result_get_op_res_gpointer (async);
|
|
if (!discover->realms) {
|
|
g_set_error (error, UM_REALM_ERROR, UM_REALM_ERROR_GENERIC,
|
|
_("No such domain or realm found"));
|
|
return NULL;
|
|
}
|
|
|
|
realms = g_list_reverse (discover->realms);
|
|
discover->realms = NULL;
|
|
return realms;
|
|
}
|
|
|
|
GList *
|
|
um_realm_manager_get_realms (UmRealmManager *self)
|
|
{
|
|
GList *objects;
|
|
GList *realms = NULL;
|
|
GList *l;
|
|
|
|
g_return_val_if_fail (UM_IS_REALM_MANAGER (self), NULL);
|
|
|
|
objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self));
|
|
for (l = objects; l != NULL; l = g_list_next (l)) {
|
|
if (is_realm_with_kerberos_and_membership (l->data))
|
|
realms = g_list_prepend (realms, g_object_ref (l->data));
|
|
}
|
|
|
|
g_list_free_full (objects, g_object_unref);
|
|
return realms;
|
|
}
|
|
|
|
static void
|
|
string_replace (GString *string,
|
|
const gchar *find,
|
|
const gchar *replace)
|
|
{
|
|
const gchar *at;
|
|
gssize pos;
|
|
|
|
at = strstr (string->str, find);
|
|
if (at != NULL) {
|
|
pos = at - string->str;
|
|
g_string_erase (string, pos, strlen (find));
|
|
g_string_insert (string, pos, replace);
|
|
}
|
|
}
|
|
|
|
gchar *
|
|
um_realm_calculate_login (UmRealmCommon *realm,
|
|
const gchar *username)
|
|
{
|
|
GString *string;
|
|
const gchar *const *formats;
|
|
gchar *login = NULL;
|
|
|
|
formats = um_realm_common_get_login_formats (realm);
|
|
if (formats[0] != NULL) {
|
|
string = g_string_new (formats[0]);
|
|
string_replace (string, "%U", username);
|
|
string_replace (string, "%D", um_realm_common_get_name (realm));
|
|
login = g_string_free (string, FALSE);
|
|
}
|
|
|
|
return login;
|
|
|
|
}
|
|
|
|
gboolean
|
|
um_realm_is_configured (UmRealmObject *realm)
|
|
{
|
|
UmRealmCommon *common;
|
|
const gchar *configured;
|
|
gboolean is;
|
|
|
|
common = um_realm_object_get_common (realm);
|
|
configured = um_realm_common_get_configured (common);
|
|
is = configured != NULL && !g_str_equal (configured, "");
|
|
g_object_unref (common);
|
|
|
|
return is;
|
|
}
|
|
|
|
static const gchar *
|
|
find_supported_credentials (UmRealmKerberosMembership *membership,
|
|
const gchar *owner)
|
|
{
|
|
const gchar *cred_owner;
|
|
const gchar *cred_type;
|
|
GVariant *supported;
|
|
GVariantIter iter;
|
|
|
|
supported = um_realm_kerberos_membership_get_supported_join_credentials (membership);
|
|
g_return_val_if_fail (supported != NULL, NULL);
|
|
|
|
g_variant_iter_init (&iter, supported);
|
|
while (g_variant_iter_loop (&iter, "(&s&s)", &cred_type, &cred_owner)) {
|
|
if (g_str_equal (owner, cred_owner)) {
|
|
if (g_str_equal (cred_type, "ccache") ||
|
|
g_str_equal (cred_type, "password")) {
|
|
return g_intern_string (cred_type);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
on_realm_join_complete (GObject *source,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
|
|
g_simple_async_result_set_op_res_gpointer (async, g_object_ref (result), g_object_unref);
|
|
g_simple_async_result_complete_in_idle (async);
|
|
g_object_unref (async);
|
|
}
|
|
|
|
static gboolean
|
|
realm_join_as_owner (UmRealmObject *realm,
|
|
const gchar *owner,
|
|
const gchar *login,
|
|
const gchar *password,
|
|
GBytes *credentials,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
UmRealmKerberosMembership *membership;
|
|
GSimpleAsyncResult *async;
|
|
GVariant *contents;
|
|
GVariant *options;
|
|
GVariant *creds;
|
|
const gchar *type;
|
|
|
|
membership = um_realm_object_get_kerberos_membership (realm);
|
|
g_return_val_if_fail (membership != NULL, FALSE);
|
|
|
|
type = find_supported_credentials (membership, owner);
|
|
if (type == NULL) {
|
|
g_object_unref (membership);
|
|
return FALSE;
|
|
}
|
|
|
|
async = g_simple_async_result_new (G_OBJECT (realm), callback, user_data,
|
|
realm_join_as_owner);
|
|
|
|
if (g_str_equal (type, "ccache")) {
|
|
contents = g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
|
|
g_bytes_get_data (credentials, NULL),
|
|
g_bytes_get_size (credentials),
|
|
TRUE, (GDestroyNotify)g_bytes_unref, credentials);
|
|
|
|
} else if (g_str_equal (type, "password")) {
|
|
contents = g_variant_new ("(ss)", login, password);
|
|
|
|
} else {
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
creds = g_variant_new ("(ssv)", type, owner, contents);
|
|
options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
|
|
|
|
um_realm_kerberos_membership_call_join (membership, creds, options,
|
|
cancellable, on_realm_join_complete,
|
|
g_object_ref (async));
|
|
|
|
g_object_unref (async);
|
|
g_object_unref (membership);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
um_realm_join_as_user (UmRealmObject *realm,
|
|
const gchar *login,
|
|
const gchar *password,
|
|
GBytes *credentials,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
g_return_val_if_fail (UM_REALM_IS_OBJECT (realm), FALSE);
|
|
g_return_val_if_fail (credentials != NULL, FALSE);
|
|
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
|
|
g_return_val_if_fail (login != NULL, FALSE);
|
|
g_return_val_if_fail (password != NULL, FALSE);
|
|
g_return_val_if_fail (credentials != NULL, FALSE);
|
|
|
|
return realm_join_as_owner (realm, "user", login, password,
|
|
credentials, cancellable, callback, user_data);
|
|
}
|
|
|
|
gboolean
|
|
um_realm_join_as_admin (UmRealmObject *realm,
|
|
const gchar *login,
|
|
const gchar *password,
|
|
GBytes *credentials,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
g_return_val_if_fail (UM_REALM_IS_OBJECT (realm), FALSE);
|
|
g_return_val_if_fail (credentials != NULL, FALSE);
|
|
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
|
|
g_return_val_if_fail (login != NULL, FALSE);
|
|
g_return_val_if_fail (password != NULL, FALSE);
|
|
g_return_val_if_fail (credentials != NULL, FALSE);
|
|
|
|
return realm_join_as_owner (realm, "administrator", login, password, credentials,
|
|
cancellable, callback, user_data);
|
|
}
|
|
|
|
gboolean
|
|
um_realm_join_finish (UmRealmObject *realm,
|
|
GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
UmRealmKerberosMembership *membership;
|
|
GError *call_error = NULL;
|
|
gchar *dbus_error;
|
|
GAsyncResult *async;
|
|
|
|
g_return_val_if_fail (UM_REALM_IS_OBJECT (realm), FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
membership = um_realm_object_get_kerberos_membership (realm);
|
|
g_return_val_if_fail (membership != NULL, FALSE);
|
|
|
|
async = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
|
|
um_realm_kerberos_membership_call_join_finish (membership, async, &call_error);
|
|
g_object_unref (membership);
|
|
|
|
if (call_error == NULL)
|
|
return TRUE;
|
|
|
|
dbus_error = g_dbus_error_get_remote_error (call_error);
|
|
if (dbus_error == NULL) {
|
|
g_propagate_error (error, call_error);
|
|
return FALSE;
|
|
}
|
|
|
|
g_dbus_error_strip_remote_error (call_error);
|
|
|
|
if (g_str_equal (dbus_error, "org.freedesktop.realmd.Error.AuthenticationFailed")) {
|
|
g_set_error (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN,
|
|
"%s", call_error->message);
|
|
g_error_free (call_error);
|
|
} else {
|
|
g_propagate_error (error, call_error);
|
|
}
|
|
|
|
g_free (dbus_error);
|
|
return FALSE;
|
|
}
|
|
|
|
typedef struct {
|
|
gchar *domain;
|
|
gchar *realm;
|
|
gchar *user;
|
|
gchar *password;
|
|
GBytes *credentials;
|
|
} LoginClosure;
|
|
|
|
static void
|
|
login_closure_free (gpointer data)
|
|
{
|
|
LoginClosure *login = data;
|
|
g_free (login->domain);
|
|
g_free (login->realm);
|
|
g_free (login->user);
|
|
g_free (login->password);
|
|
g_bytes_unref (login->credentials);
|
|
g_slice_free (LoginClosure, login);
|
|
}
|
|
|
|
static krb5_error_code
|
|
login_perform_kinit (krb5_context k5,
|
|
const gchar *realm,
|
|
const gchar *login,
|
|
const gchar *password,
|
|
const gchar *filename)
|
|
{
|
|
krb5_get_init_creds_opt *opts;
|
|
krb5_error_code code;
|
|
krb5_principal principal;
|
|
krb5_ccache ccache;
|
|
krb5_creds creds;
|
|
gchar *name;
|
|
|
|
name = g_strdup_printf ("%s@%s", login, realm);
|
|
code = krb5_parse_name (k5, name, &principal);
|
|
g_free (name);
|
|
|
|
if (code != 0)
|
|
return code;
|
|
|
|
if (filename == NULL)
|
|
code = krb5_cc_default (k5, &ccache);
|
|
else
|
|
code = krb5_cc_resolve (k5, filename, &ccache);
|
|
|
|
if (code != 0) {
|
|
krb5_free_principal (k5, principal);
|
|
return code;
|
|
}
|
|
|
|
code = krb5_get_init_creds_opt_alloc (k5, &opts);
|
|
g_return_val_if_fail (code == 0, code);
|
|
|
|
code = krb5_get_init_creds_opt_set_out_ccache (k5, opts, ccache);
|
|
g_return_val_if_fail (code == 0, code);
|
|
|
|
code = krb5_get_init_creds_password (k5, &creds, principal,
|
|
(char *)password,
|
|
NULL, 0, 0, NULL, opts);
|
|
|
|
krb5_get_init_creds_opt_free (k5, opts);
|
|
krb5_cc_close (k5, ccache);
|
|
krb5_free_principal (k5, principal);
|
|
|
|
if (code == 0)
|
|
krb5_free_cred_contents (k5, &creds);
|
|
|
|
return code;
|
|
}
|
|
|
|
static void
|
|
kinit_thread_func (GSimpleAsyncResult *async,
|
|
GObject *object,
|
|
GCancellable *cancellable)
|
|
{
|
|
LoginClosure *login = g_simple_async_result_get_op_res_gpointer (async);
|
|
krb5_context k5 = NULL;
|
|
krb5_error_code code;
|
|
GError *error = NULL;
|
|
gchar *filename = NULL;
|
|
gchar *contents;
|
|
gsize length;
|
|
gint temp_fd;
|
|
|
|
filename = g_build_filename (g_get_user_runtime_dir (),
|
|
"um-krb5-creds.XXXXXX", NULL);
|
|
temp_fd = g_mkstemp_full (filename, O_RDWR, S_IRUSR | S_IWUSR);
|
|
if (temp_fd == -1) {
|
|
g_warning ("Couldn't create credential cache file: %s: %s",
|
|
filename, g_strerror (errno));
|
|
g_free (filename);
|
|
filename = NULL;
|
|
} else {
|
|
close (temp_fd);
|
|
}
|
|
|
|
code = krb5_init_context (&k5);
|
|
if (code == 0) {
|
|
code = login_perform_kinit (k5, login->realm, login->user,
|
|
login->password, filename);
|
|
}
|
|
|
|
switch (code) {
|
|
case 0:
|
|
if (filename != NULL) {
|
|
g_file_get_contents (filename, &contents, &length, &error);
|
|
if (error == NULL) {
|
|
login->credentials = g_bytes_new_take (contents, length);
|
|
} else {
|
|
g_warning ("Couldn't read credential cache: %s", error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
|
|
case KRB5KDC_ERR_CLIENT_REVOKED:
|
|
case KRB5KDC_ERR_KEY_EXP:
|
|
case KRB5KDC_ERR_POLICY:
|
|
case KRB5KDC_ERR_ETYPE_NOSUPP:
|
|
g_simple_async_result_set_error (async, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN,
|
|
_("Cannot log in as %s at the %s domain"),
|
|
login->user, login->domain);
|
|
break;
|
|
case KRB5KDC_ERR_PREAUTH_FAILED:
|
|
g_simple_async_result_set_error (async, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD,
|
|
_("Invalid password, please try again"));
|
|
break;
|
|
default:
|
|
g_simple_async_result_set_error (async, UM_REALM_ERROR, UM_REALM_ERROR_GENERIC,
|
|
_("Couldn't connect to the %s domain: %s"),
|
|
login->domain, krb5_get_error_message (k5, code));
|
|
break;
|
|
}
|
|
|
|
if (filename) {
|
|
g_unlink (filename);
|
|
g_free (filename);
|
|
}
|
|
|
|
if (k5)
|
|
krb5_free_context (k5);
|
|
}
|
|
|
|
void
|
|
um_realm_login (UmRealmObject *realm,
|
|
const gchar *user,
|
|
const gchar *password,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GSimpleAsyncResult *async;
|
|
LoginClosure *login;
|
|
UmRealmKerberos *kerberos;
|
|
|
|
g_return_if_fail (UM_REALM_IS_OBJECT (realm));
|
|
g_return_if_fail (user != NULL);
|
|
g_return_if_fail (password != NULL);
|
|
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
|
|
|
|
kerberos = um_realm_object_get_kerberos (realm);
|
|
g_return_if_fail (kerberos != NULL);
|
|
|
|
async = g_simple_async_result_new (NULL, callback, user_data,
|
|
um_realm_login);
|
|
login = g_slice_new0 (LoginClosure);
|
|
login->domain = g_strdup (um_realm_kerberos_get_domain_name (kerberos));
|
|
login->realm = g_strdup (um_realm_kerberos_get_realm_name (kerberos));
|
|
login->user = g_strdup (user);
|
|
login->password = g_strdup (password);
|
|
g_simple_async_result_set_op_res_gpointer (async, login, login_closure_free);
|
|
|
|
g_simple_async_result_set_handle_cancellation (async, TRUE);
|
|
g_simple_async_result_run_in_thread (async, kinit_thread_func,
|
|
G_PRIORITY_DEFAULT, cancellable);
|
|
|
|
g_object_unref (async);
|
|
g_object_unref (kerberos);
|
|
}
|
|
|
|
gboolean
|
|
um_realm_login_finish (GAsyncResult *result,
|
|
GBytes **credentials,
|
|
GError **error)
|
|
{
|
|
GSimpleAsyncResult *async;
|
|
LoginClosure *login;
|
|
|
|
g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
|
|
um_realm_login), FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
async = G_SIMPLE_ASYNC_RESULT (result);
|
|
if (g_simple_async_result_propagate_error (async, error))
|
|
return FALSE;
|
|
|
|
login = g_simple_async_result_get_op_res_gpointer (async);
|
|
if (credentials) {
|
|
if (login->credentials)
|
|
*credentials = g_bytes_ref (login->credentials);
|
|
else
|
|
*credentials = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|