From 0ccab22ee39bd98b63d31c7457d91778b98080bf Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 22 Oct 2014 11:06:19 -0400
Subject: [PATCH 1/3] kerberos: zero initialize operation structure
We need zero initialize the structure so we don't end up
freeing unused members when cleaning up the operation.
---
src/goaidentity/goakerberosidentitymanager.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/goaidentity/goakerberosidentitymanager.c b/src/goaidentity/goakerberosidentitymanager.c
index 7cc90b9..c4da420 100644
--- a/src/goaidentity/goakerberosidentitymanager.c
+++ b/src/goaidentity/goakerberosidentitymanager.c
@@ -102,61 +102,61 @@ typedef struct
GoaKerberosIdentityManager *manager;
GoaIdentity *identity;
} IdentitySignalWork;
static GoaIdentityManager *goa_kerberos_identity_manager_singleton;
static void identity_manager_interface_init (GoaIdentityManagerInterface *
interface);
static void initable_interface_init (GInitableIface *interface);
static void on_identity_expired (GoaIdentity *identity,
GoaKerberosIdentityManager *self);
G_DEFINE_TYPE_WITH_CODE (GoaKerberosIdentityManager,
goa_kerberos_identity_manager,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GOA_TYPE_IDENTITY_MANAGER,
identity_manager_interface_init)
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
initable_interface_init));
#define FALLBACK_POLLING_INTERVAL 5
static Operation *
operation_new (GoaKerberosIdentityManager *self,
GCancellable *cancellable,
OperationType type,
GSimpleAsyncResult *result)
{
Operation *operation;
- operation = g_slice_new (Operation);
+ operation = g_slice_new0 (Operation);
operation->manager = self;
operation->type = type;
if (cancellable == NULL)
cancellable = g_cancellable_new ();
else
g_object_ref (cancellable);
operation->cancellable = cancellable;
if (result != NULL)
g_object_ref (result);
operation->result = result;
operation->identity = NULL;
return operation;
}
static void
operation_free (Operation *operation)
{
g_clear_object (&operation->cancellable);
if (operation->type != OPERATION_TYPE_SIGN_IN &&
operation->type != OPERATION_TYPE_GET_IDENTITY)
g_clear_object (&operation->identity);
else
g_clear_pointer (&operation->identifier, g_free);
--
1.8.3.1
From 8e36b5743bf1e20636a67f406f4937ccc132e50b Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 21 Oct 2014 10:38:17 -0400
Subject: [PATCH 2/3] ticketing: add new details property
This commit adds a new "details" variant for attaching metadata
about tickets getting requested via the ticketing interface.
This will give the kerberos account provider a place to tuck away
kerberos-specific preauthentication configuration for the tickets
associated with smartcard backed kerberos accounts.
---
data/dbus-interfaces.xml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/data/dbus-interfaces.xml b/data/dbus-interfaces.xml
index fa4101f..e4d39c4 100644
--- a/data/dbus-interfaces.xml
+++ b/data/dbus-interfaces.xml
@@ -604,40 +604,42 @@
<!-- AcceptSslErrors:
@since: 3.8.0
Accept SSL/TLS errors caused by invalid certificates.
-->
<property name="AcceptSslErrors" type="b" access="read"/>
<!-- Host:
The Exchange server to use. This is always a domain name.
Use this to determine the <ulink
url="http://msdn.microsoft.com/en-us/library/gg591268%28v.exchg.140%29.aspx">
Autodiscover</ulink> service endpoints. eg. if
#org.gnome.OnlineAccounts.Exchange:Host is
<literal>bar.com</literal>, then the possible endpoints are
<literal>https://bar.com/autodiscover/autodiscover.xml
</literal> and <literal>
https://autodiscover.bar.com/autodiscover/autodiscover.xml
</literal>.
-->
<property name="Host" type="s" access="read"/>
</interface>
<!--
org.gnome.OnlineAccounts.Ticketing:
@since: 3.6.0
An account object implements this interface if it provides
ticketing capabilities.
-->
<interface name="org.gnome.OnlineAccounts.Ticketing">
+ <property name="details" type="a{ss}" access="read"/>
+
<!--
GetTicket:
Use this method to obtain an ticket that can be used to
access resources for the account.
-->
<method name="GetTicket"/>
</interface>
</node>
--
1.8.3.1
From 2cf3c36818fa995791d99c6512b258099cec2879 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 21 Oct 2014 10:46:50 -0400
Subject: [PATCH 3/3] kerberos: support refreshing smartcard authenticated
kerberos tickets
Right now gnome-online-accounts doesn't manager smartcard based
kerberos credentials very well. The sign-in button just fails.
This commit adds support for tickets granted through smartcards.
Note, at the moment we don't provide a way to add new smartcard
based accounts, merely, manage existing ones that are added implicitly
by logging in with a smartcard, or by explicit kinit.
---
src/goabackend/goakerberosprovider.c | 43 +++++++++++++++++
src/goaidentity/goaidentitymanager.c | 2 +
src/goaidentity/goaidentitymanager.h | 2 +
src/goaidentity/goaidentityservice.c | 19 +++++++-
src/goaidentity/goakerberosidentity.c | 71 +++++++++++++++++++++++++++-
src/goaidentity/goakerberosidentity.h | 2 +
src/goaidentity/goakerberosidentitymanager.c | 14 ++++--
7 files changed, 146 insertions(+), 7 deletions(-)
diff --git a/src/goabackend/goakerberosprovider.c b/src/goabackend/goakerberosprovider.c
index 7fe77f6..b125914 100644
--- a/src/goabackend/goakerberosprovider.c
+++ b/src/goabackend/goakerberosprovider.c
@@ -345,93 +345,100 @@ ensure_object_manager (GoaKerberosProvider *self,
"cancellable",
cancellable);
if (self->object_manager != NULL)
{
g_simple_async_result_set_op_res_gpointer (operation_result,
g_object_ref (self->object_manager),
(GDestroyNotify)
g_object_unref);
g_simple_async_result_complete_in_idle (operation_result);
g_object_unref (operation_result);
return;
}
goa_identity_service_object_manager_client_new_for_bus (G_BUS_TYPE_SESSION,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
"org.gnome.Identity",
"/org/gnome/Identity",
cancellable,
(GAsyncReadyCallback)
on_object_manager_ensured,
operation_result);
}
static void
on_secret_keys_exchanged_for_sign_in (GoaKerberosProvider *self,
GAsyncResult *result,
GSimpleAsyncResult *operation_result)
{
const char *identifier;
const char *password;
+ const char *preauth_source;
GCancellable *cancellable;
GError *error;
GVariantBuilder details;
error = NULL;
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
&error))
{
g_simple_async_result_take_error (operation_result, error);
g_simple_async_result_complete_in_idle (operation_result);
g_object_unref (operation_result);
return;
}
cancellable = g_object_get_data (G_OBJECT (operation_result), "cancellable");
password = g_object_get_data (G_OBJECT (operation_result), "password");
+ preauth_source = g_object_get_data (G_OBJECT (operation_result), "preauthentication-source");
identifier = g_simple_async_result_get_source_tag (operation_result);
g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
if (password != NULL)
{
GcrSecretExchange *secret_exchange;
char *secret;
secret_exchange = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
secret = gcr_secret_exchange_send (secret_exchange, password, -1);
g_variant_builder_add (&details, "{ss}", "initial-password", secret);
g_free (secret);
}
+ if (preauth_source != NULL)
+ {
+ g_variant_builder_add (&details, "{ss}", "preauthentication-source", preauth_source);
+ }
+
goa_identity_service_manager_call_sign_in (self->identity_manager,
identifier,
g_variant_builder_end (&details),
cancellable,
(GAsyncReadyCallback)
on_identity_signed_in,
operation_result);
}
static void
on_secret_keys_exchanged (GoaIdentityServiceManager *manager,
GAsyncResult *result,
GSimpleAsyncResult *operation_result)
{
GcrSecretExchange *secret_exchange;
char *return_key;
GError *error;
secret_exchange = g_simple_async_result_get_source_tag (operation_result);
error = NULL;
if (!goa_identity_service_manager_call_exchange_secret_keys_finish (manager,
&return_key,
result,
&error))
{
g_object_unref (secret_exchange);
g_simple_async_result_take_error (operation_result, error);
g_simple_async_result_complete_in_idle (operation_result);
@@ -513,82 +520,88 @@ on_identity_manager_ensured_for_sign_in (GoaKerberosProvider *self,
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
&error))
{
g_simple_async_result_take_error (operation_result, error);
g_simple_async_result_complete_in_idle (operation_result);
g_object_unref (operation_result);
return;
}
manager = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
if (self->identity_manager == NULL)
self->identity_manager = g_object_ref (manager);
cancellable = g_object_get_data (G_OBJECT (operation_result), "cancellable");
password = g_object_get_data (G_OBJECT (operation_result), "password");
exchange_secret_keys (self,
password,
cancellable,
(GAsyncReadyCallback)
on_secret_keys_exchanged_for_sign_in,
operation_result);
}
static void
sign_in_identity (GoaKerberosProvider *self,
const char *identifier,
const char *password,
+ const char *preauth_source,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *operation_result;
operation_result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
(gpointer)
identifier);
g_simple_async_result_set_check_cancellable (operation_result, cancellable);
g_object_set_data (G_OBJECT (operation_result),
"cancellable",
cancellable);
g_object_set_data (G_OBJECT (operation_result),
"password",
(gpointer)
password);
+ g_object_set_data_full (G_OBJECT (operation_result),
+ "preauthentication-source",
+ g_strdup (preauth_source),
+ g_free);
+
ensure_identity_manager (self,
cancellable,
(GAsyncReadyCallback)
on_identity_manager_ensured_for_sign_in,
operation_result);
}
static void
on_object_manager_ensured_for_look_up (GoaKerberosProvider *self,
GAsyncResult *result,
GSimpleAsyncResult *operation_result)
{
GDBusObjectManager *manager;
const char *identifier;
GList *objects, *node;
GError *error;
gboolean found;
error = NULL;
found = FALSE;
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
&error))
{
g_simple_async_result_take_error (operation_result, error);
g_simple_async_result_complete_in_idle (operation_result);
g_object_unref (operation_result);
return;
}
@@ -666,128 +679,141 @@ look_up_identity (GoaKerberosProvider *self,
(GAsyncReadyCallback)
on_object_manager_ensured_for_look_up,
operation_result);
}
static void
on_account_signed_in (GoaProvider *provider,
GAsyncResult *result,
SignInRequest *request)
{
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
&request->error))
{
g_main_loop_quit (request->loop);
return;
}
g_main_loop_quit (request->loop);
}
static gboolean
get_ticket_sync (GoaKerberosProvider *self,
GoaObject *object,
gboolean is_interactive,
GCancellable *cancellable,
GError **error)
{
GVariant *credentials;
GError *lookup_error;
GoaAccount *account;
+ GoaTicketing *ticketing;
+ GVariant *details;
const char *identifier;
const char *password;
+ const char *preauth_source;
SignInRequest request;
gboolean ret;
ret = FALSE;
account = goa_object_peek_account (object);
identifier = goa_account_get_identity (account);
+
+ ticketing = goa_object_get_ticketing (GOA_OBJECT (object));
+ details = goa_ticketing_get_details (ticketing);
+
+ preauth_source = NULL;
+ g_variant_lookup (details, "preauthentication-source", "&s", &preauth_source);
+
password = NULL;
lookup_error = NULL;
credentials = goa_utils_lookup_credentials_sync (GOA_PROVIDER (self),
object,
cancellable,
&lookup_error);
if (credentials == NULL && !is_interactive)
{
if (lookup_error != NULL)
g_propagate_error (error, lookup_error);
else
g_set_error (error,
GOA_ERROR,
GOA_ERROR_NOT_AUTHORIZED,
_("Could not find saved credentials for principal `%s' in keyring"), identifier);
goto out;
}
else if (credentials != NULL)
{
gboolean has_password;
has_password = g_variant_lookup (credentials, "password", "&s", &password);
if (!has_password && !is_interactive)
{
g_set_error (error,
GOA_ERROR,
GOA_ERROR_NOT_AUTHORIZED,
_("Did not find password for principal `%s' in credentials"),
identifier);
goto out;
}
}
memset (&request, 0, sizeof (SignInRequest));
request.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
request.error = NULL;
sign_in_identity (self,
identifier,
password,
+ preauth_source,
cancellable,
(GAsyncReadyCallback)
on_account_signed_in,
&request);
g_main_loop_run (request.loop);
g_main_loop_unref (request.loop);
if (request.error != NULL)
{
g_propagate_error (error, request.error);
goto out;
}
ret = TRUE;
out:
+ g_clear_object (&ticketing);
+
if (credentials != NULL)
g_variant_unref (credentials);
return ret;
}
static void
notify_is_temporary_cb (GObject *object, GParamSpec *pspec, gpointer user_data)
{
GoaAccount *account;
gboolean is_temporary;
account = GOA_ACCOUNT (object);
g_object_get (account, "is-temporary", &is_temporary, NULL);
/* Toggle IsTemporary */
goa_utils_keyfile_set_boolean (account, "IsTemporary", is_temporary);
/* Set/unset SessionId */
if (is_temporary)
{
GDBusConnection *connection;
const gchar *guid;
connection = G_DBUS_CONNECTION (user_data);
guid = g_dbus_connection_get_guid (connection);
goa_utils_keyfile_set_string (account, "SessionId", guid);
}
else
goa_utils_keyfile_remove_key (account, "SessionId");
@@ -831,69 +857,79 @@ build_object (GoaProvider *provider,
GDBusConnection *connection,
gboolean just_added,
GError **error)
{
GoaAccount *account;
GoaTicketing *ticketing;
gboolean ticketing_enabled;
gboolean ret;
ticketing = NULL;
ret = FALSE;
if (!GOA_PROVIDER_CLASS (goa_kerberos_provider_parent_class)->build_object (provider,
object,
key_file,
group,
connection,
just_added,
error))
goto out;
account = goa_object_get_account (GOA_OBJECT (object));
ticketing = goa_object_get_ticketing (GOA_OBJECT (object));
ticketing_enabled = g_key_file_get_boolean (key_file, group, "TicketingEnabled", NULL);
if (ticketing_enabled)
{
if (ticketing == NULL)
{
+ char *preauthentication_source;
+ GVariantBuilder details;
+
ticketing = goa_ticketing_skeleton_new ();
g_signal_connect (ticketing,
"handle-get-ticket",
G_CALLBACK (on_handle_get_ticket),
NULL);
goa_object_skeleton_set_ticketing (object, ticketing);
+ g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
+
+ preauthentication_source = g_key_file_get_string (key_file, group, "PreauthenticationSource", NULL);
+ if (preauthentication_source)
+ g_variant_builder_add (&details, "{ss}", "preauthentication-source", preauthentication_source);
+
+ g_object_set (G_OBJECT (ticketing), "details", g_variant_builder_end (&details), NULL);
}
}
else if (ticketing != NULL)
{
goa_object_skeleton_set_ticketing (object, NULL);
}
if (just_added)
{
goa_account_set_ticketing_disabled (account, !ticketing_enabled);
g_signal_connect (account,
"notify::is-temporary",
G_CALLBACK (notify_is_temporary_cb),
connection);
g_signal_connect (account,
"notify::ticketing-disabled",
G_CALLBACK (goa_util_account_notify_property_cb),
"TicketingEnabled");
}
ret = TRUE;
out:
g_clear_object (&ticketing);
return ret;
}
@@ -1266,99 +1302,106 @@ on_initial_sign_in_done (GoaKerberosProvider *self,
*/
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&builder,
"{sv}",
"password",
g_variant_new_string (password));
error = NULL;
goa_utils_store_credentials_for_object_sync (GOA_PROVIDER (self),
object,
g_variant_builder_end (&builder),
NULL,
NULL);
}
}
g_simple_async_result_complete_in_idle (operation_result);
g_object_unref (operation_result);
}
static void
on_system_prompt_answered_for_initial_sign_in (GcrPrompt *prompt,
GAsyncResult *result,
GSimpleAsyncResult *operation_result)
{
GoaKerberosProvider *self;
GCancellable *cancellable;
GError *error;
const char *principal;
const char *password;
+ const char *preauth_source;
GcrSecretExchange *secret_exchange;
self = GOA_KERBEROS_PROVIDER (g_async_result_get_source_object (G_ASYNC_RESULT (operation_result)));
principal = g_object_get_data (G_OBJECT (operation_result), "principal");
cancellable = g_object_get_data (G_OBJECT (operation_result), "cancellable");
+ /* We currently don't prompt the user to choose a preauthentication source during initial sign in
+ * so we assume there's no preauthentication source
+ */
+ preauth_source = NULL;
+
error = NULL;
password = gcr_prompt_password_finish (prompt, result, &error);
if (password == NULL)
{
gcr_system_prompt_close (GCR_SYSTEM_PROMPT (prompt), NULL, NULL);
if (error != NULL)
g_simple_async_result_take_error (operation_result, error);
else
g_cancellable_cancel (cancellable);
g_simple_async_result_complete_in_idle (operation_result);
g_object_unref (operation_result);
return;
}
secret_exchange = gcr_system_prompt_get_secret_exchange (GCR_SYSTEM_PROMPT (prompt));
g_object_set_data_full (G_OBJECT (operation_result),
"secret-exchange",
g_object_ref (secret_exchange),
(GDestroyNotify)
g_object_unref);
g_object_set_data (G_OBJECT (operation_result),
"remember-password",
GINT_TO_POINTER (gcr_prompt_get_choice_chosen (prompt)));
gcr_system_prompt_close (GCR_SYSTEM_PROMPT (prompt), NULL, NULL);
sign_in_identity (self,
principal,
password,
+ preauth_source,
cancellable,
(GAsyncReadyCallback)
on_initial_sign_in_done,
operation_result);
}
static void
on_system_prompt_open_for_initial_sign_in (GcrSystemPrompt *system_prompt,
GAsyncResult *result,
GSimpleAsyncResult *operation_result)
{
GCancellable *cancellable;
GcrPrompt *prompt;
GError *error;
cancellable = g_object_get_data (G_OBJECT (operation_result), "cancellable");
error = NULL;
prompt = gcr_system_prompt_open_finish (result, &error);
if (prompt == NULL)
{
g_simple_async_result_take_error (operation_result, error);
g_simple_async_result_complete_in_idle (operation_result);
g_object_unref (operation_result);
return;
}
gcr_prompt_set_title (prompt, _("Log In to Realm"));
diff --git a/src/goaidentity/goaidentitymanager.c b/src/goaidentity/goaidentitymanager.c
index b71fb23..8fa7b22 100644
--- a/src/goaidentity/goaidentitymanager.c
+++ b/src/goaidentity/goaidentitymanager.c
@@ -178,70 +178,72 @@ goa_identity_manager_list_identities_finish (GoaIdentityManager *self,
result,
error);
}
void
goa_identity_manager_renew_identity (GoaIdentityManager *self,
GoaIdentity *identity,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GOA_IDENTITY_MANAGER_GET_IFACE (self)->renew_identity (self,
identity,
cancellable,
callback,
user_data);
}
void
goa_identity_manager_renew_identity_finish (GoaIdentityManager *self,
GAsyncResult *result,
GError **error)
{
GOA_IDENTITY_MANAGER_GET_IFACE (self)->renew_identity_finish (self, result, error);
}
void
goa_identity_manager_sign_identity_in (GoaIdentityManager *self,
const char *identifier,
gconstpointer initial_password,
+ const char *preauth_source,
GoaIdentitySignInFlags flags,
GoaIdentityInquiryFunc inquiry_func,
gpointer inquiry_data,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GOA_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_in (self,
identifier,
initial_password,
+ preauth_source,
flags,
inquiry_func,
inquiry_data,
cancellable,
callback,
user_data);
}
GoaIdentity *
goa_identity_manager_sign_identity_in_finish (GoaIdentityManager *self,
GAsyncResult *result,
GError **error)
{
return GOA_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_in_finish (self,
result,
error);
}
void
goa_identity_manager_sign_identity_out (GoaIdentityManager *self,
GoaIdentity *identity,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GOA_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_out (self,
identity,
cancellable,
callback,
user_data);
diff --git a/src/goaidentity/goaidentitymanager.h b/src/goaidentity/goaidentitymanager.h
index 5b60c24..05e6ca8 100644
--- a/src/goaidentity/goaidentitymanager.h
+++ b/src/goaidentity/goaidentitymanager.h
@@ -54,60 +54,61 @@ struct _GoaIdentityManagerInterface
GoaIdentity *identity);
void (* identity_refreshed) (GoaIdentityManager *identity_manager,
GoaIdentity *identity);
void (* identity_needs_renewal) (GoaIdentityManager *identity_manager,
GoaIdentity *identity);
void (* identity_expiring) (GoaIdentityManager *identity_manager,
GoaIdentity *identity);
void (* identity_expired) (GoaIdentityManager *identity_manager,
GoaIdentity *identity);
/* Virtual Functions */
void (* get_identity) (GoaIdentityManager *identity_manager,
const char *identifier,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GoaIdentity * (* get_identity_finish) (GoaIdentityManager *identity_manager,
GAsyncResult *result,
GError **error);
void (* list_identities) (GoaIdentityManager *identity_manager,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GList * (* list_identities_finish) (GoaIdentityManager *identity_manager,
GAsyncResult *result,
GError **error);
void (* sign_identity_in) (GoaIdentityManager *identity_manager,
const char *identifier,
gconstpointer initial_password,
+ const char *preauth_source,
GoaIdentitySignInFlags flags,
GoaIdentityInquiryFunc inquiry_func,
gpointer inquiry_data,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GoaIdentity * (* sign_identity_in_finish) (GoaIdentityManager *identity_manager,
GAsyncResult *result,
GError **error);
void (* sign_identity_out) (GoaIdentityManager *identity_manager,
GoaIdentity *identity,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
void (* sign_identity_out_finish) (GoaIdentityManager *identity_manager,
GAsyncResult *result,
GError **error);
void (* renew_identity) (GoaIdentityManager *identity_manager,
GoaIdentity *identity,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
void (* renew_identity_finish) (GoaIdentityManager *identity_manager,
GAsyncResult *result,
GError **error);
char * (* name_identity) (GoaIdentityManager *identity_manager,
GoaIdentity *identity);
@@ -117,60 +118,61 @@ typedef enum
{
GOA_IDENTITY_MANAGER_ERROR_INITIALIZING,
GOA_IDENTITY_MANAGER_ERROR_IDENTITY_NOT_FOUND,
GOA_IDENTITY_MANAGER_ERROR_CREATING_IDENTITY,
GOA_IDENTITY_MANAGER_ERROR_ACCESSING_CREDENTIALS,
GOA_IDENTITY_MANAGER_ERROR_UNSUPPORTED_CREDENTIALS
} GoaIdentityManagerError;
GType goa_identity_manager_get_type (void);
GQuark goa_identity_manager_error_quark (void);
void goa_identity_manager_get_identity (GoaIdentityManager *identity_manager,
const char *identifier,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GoaIdentity *goa_identity_manager_get_identity_finish (GoaIdentityManager *identity_manager,
GAsyncResult *result,
GError **error);
void goa_identity_manager_list_identities (GoaIdentityManager *identity_manager,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GList *goa_identity_manager_list_identities_finish (GoaIdentityManager *identity_manager,
GAsyncResult *result,
GError **error);
void goa_identity_manager_sign_identity_in (GoaIdentityManager *identity_manager,
const char *identifier,
gconstpointer initial_password,
+ const char *preauth_source,
GoaIdentitySignInFlags flags,
GoaIdentityInquiryFunc inquiry_func,
gpointer inquiry_data,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GoaIdentity *goa_identity_manager_sign_identity_in_finish (GoaIdentityManager *identity_manager,
GAsyncResult *result,
GError **error);
void goa_identity_manager_sign_identity_out (GoaIdentityManager *identity_manager,
GoaIdentity *identity,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
void goa_identity_manager_sign_identity_out_finish (GoaIdentityManager *identity_manager,
GAsyncResult *result,
GError **error);
void goa_identity_manager_renew_identity (GoaIdentityManager *identity_manager,
GoaIdentity *identity,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
void goa_identity_manager_renew_identity_finish (GoaIdentityManager *identity_manager,
GAsyncResult *result,
GError **error);
char *goa_identity_manager_name_identity (GoaIdentityManager *identity_manager,
GoaIdentity *identity);
diff --git a/src/goaidentity/goaidentityservice.c b/src/goaidentity/goaidentityservice.c
index d51d6bd..b570e09 100644
--- a/src/goaidentity/goaidentityservice.c
+++ b/src/goaidentity/goaidentityservice.c
@@ -38,60 +38,61 @@
#include "goaidentityutils.h"
#include "goakerberosidentitymanager.h"
#include "goalogging.h"
struct _GoaIdentityServicePrivate
{
GDBusConnection *connection;
GDBusObjectManagerServer *object_manager_server;
guint bus_id;
GoaIdentityManager *identity_manager;
GHashTable *watched_client_connections;
GHashTable *key_holders;
GHashTable *pending_temporary_account_results;
/* FIXME: This is a little ucky, since the goa service
* is in process, we should able to use direct calls.
*/
GoaClient *client;
GoaManager *accounts_manager;
};
static void identity_service_manager_interface_init (GoaIdentityServiceManagerIface *interface);
static void
sign_in (GoaIdentityService *self,
const char *identifier,
gconstpointer initial_password,
+ const char *preauth_source,
GoaIdentitySignInFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
G_DEFINE_TYPE_WITH_CODE (GoaIdentityService,
goa_identity_service,
GOA_IDENTITY_SERVICE_TYPE_MANAGER_SKELETON,
G_IMPLEMENT_INTERFACE (GOA_IDENTITY_SERVICE_TYPE_MANAGER,
identity_service_manager_interface_init));
static char *
get_object_path_for_identity (GoaIdentityService *self,
GoaIdentity *identity)
{
const char *identifier;
char *escaped_identifier;
char *object_path;
identifier = goa_identity_get_identifier (identity);
escaped_identifier = goa_identity_utils_escape_object_path (identifier,
strlen (identifier));
object_path = g_strdup_printf ("/org/gnome/Identity/Identities/%s", escaped_identifier);
g_free (escaped_identifier);
return object_path;
}
static char *
export_identity (GoaIdentityService *self,
@@ -290,151 +291,158 @@ ensure_account_credentials (GoaIdentityService *self,
on_credentials_ensured,
self);
}
static void
on_sign_in_handled (GoaIdentityService *self,
GAsyncResult *result,
GDBusMethodInvocation *invocation)
{
GError *error = NULL;
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), &error))
{
g_dbus_method_invocation_take_error (invocation, error);
}
else
{
const char *object_path;
object_path = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
goa_identity_service_manager_complete_sign_in (GOA_IDENTITY_SERVICE_MANAGER (self),
invocation,
object_path);
}
}
static void
read_sign_in_details (GoaIdentityServiceManager *manager,
GVariant *details,
GoaIdentitySignInFlags *flags,
- char **secret_key)
+ char **secret_key,
+ char **preauth_source)
{
GVariantIter iter;
char *key;
char *value;
*flags = GOA_IDENTITY_SIGN_IN_FLAGS_NONE;
g_variant_iter_init (&iter, details);
while (g_variant_iter_loop (&iter, "{ss}", &key, &value))
{
if (g_strcmp0 (key, "initial-password") == 0)
*secret_key = g_strdup (value);
+ else if (g_strcmp0 (key, "preauthentication-source") == 0)
+ *preauth_source = g_strdup (value);
else if (g_strcmp0 (key, "disallow-renewal") == 0
&& g_strcmp0 (value, "true") == 0)
*flags |= GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_RENEWAL;
else if (g_strcmp0 (key, "disallow-forwarding") == 0
&& g_strcmp0 (value, "true") == 0)
*flags |= GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_FORWARDING;
else if (g_strcmp0 (key, "disallow-proxying") == 0
&& g_strcmp0 (value, "true") == 0)
*flags |= GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_PROXYING;
}
}
static gboolean
goa_identity_service_handle_sign_in (GoaIdentityServiceManager *manager,
GDBusMethodInvocation *invocation,
const char *identifier,
GVariant *details)
{
GoaIdentityService *self = GOA_IDENTITY_SERVICE (manager);
GSimpleAsyncResult *operation_result;
GoaIdentitySignInFlags flags;
char *secret_key;
+ char *preauth_source;
gconstpointer initial_password;
GCancellable *cancellable;
secret_key = NULL;
+ preauth_source = NULL;
initial_password = NULL;
- read_sign_in_details (manager, details, &flags, &secret_key);
+ read_sign_in_details (manager, details, &flags, &secret_key, &preauth_source);
if (secret_key != NULL)
{
GcrSecretExchange *secret_exchange;
secret_exchange = g_hash_table_lookup (self->priv->key_holders,
g_dbus_method_invocation_get_sender (invocation));
if (secret_exchange == NULL)
{
g_free (secret_key);
g_dbus_method_invocation_return_error (invocation,
GOA_IDENTITY_MANAGER_ERROR,
GOA_IDENTITY_MANAGER_ERROR_ACCESSING_CREDENTIALS,
_("initial secret passed before secret key exchange"));
return TRUE;
}
gcr_secret_exchange_receive (secret_exchange, secret_key);
g_free (secret_key);
initial_password = gcr_secret_exchange_get_secret (secret_exchange, NULL);
}
operation_result = g_simple_async_result_new (G_OBJECT (self),
(GAsyncReadyCallback)
on_sign_in_handled,
g_object_ref (invocation),
g_strdup (identifier));
cancellable = g_cancellable_new ();
g_object_set_data (G_OBJECT (operation_result),
"cancellable",
cancellable);
g_object_set_data (G_OBJECT (operation_result),
"initial-password",
(gpointer)
initial_password);
g_object_set_data (G_OBJECT (operation_result),
"flags",
GINT_TO_POINTER ((int) flags));
sign_in (self,
identifier,
initial_password,
+ preauth_source,
flags,
cancellable,
(GAsyncReadyCallback)
on_sign_in_done,
operation_result);
+ g_free (preauth_source);
g_object_unref (cancellable);
return TRUE;
}
static void
on_sign_out_handled (GoaIdentityService *self,
GAsyncResult *result,
GDBusMethodInvocation *invocation)
{
GError *error;
error = NULL;
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), &error))
g_dbus_method_invocation_take_error (invocation, error);
else
goa_identity_service_manager_complete_sign_out (GOA_IDENTITY_SERVICE_MANAGER (self),
invocation);
}
static void
on_identity_signed_out (GoaIdentityManager *manager,
GAsyncResult *result,
GSimpleAsyncResult *operation_result)
{
GoaIdentityService *self;
GError *error;
GoaIdentity *identity;
const char *identifier;
GoaObject *object;
@@ -846,117 +854,122 @@ on_account_added (GoaManager *manager,
return;
}
if (object_path != NULL && object_path[0] != '\0')
{
goa_debug ("Created account for identity with object path %s", object_path);
object_manager = goa_client_get_object_manager (self->priv->client);
object = GOA_OBJECT (g_dbus_object_manager_get_object (object_manager,
object_path));
g_free (object_path);
}
if (object == NULL)
g_simple_async_result_set_op_res_gpointer (operation_result, NULL, NULL);
else
g_simple_async_result_set_op_res_gpointer (operation_result,
object,
(GDestroyNotify)
g_object_unref);
g_simple_async_result_complete_in_idle (operation_result);
g_object_unref (operation_result);
}
static void
add_temporary_account (GoaIdentityService *self,
GoaIdentity *identity)
{
char *realm;
+ char *preauth_source;
const char *principal;
char *principal_for_display;
GSimpleAsyncResult *operation_result;
GVariantBuilder credentials;
GVariantBuilder details;
GoaObject *object;
principal = goa_identity_get_identifier (identity);
object = g_hash_table_lookup (self->priv->pending_temporary_account_results,
principal);
if (object != NULL)
{
goa_debug ("GoaIdentityService: would add temporary identity %s, but it's already pending", principal);
return;
}
goa_debug ("GoaIdentityService: adding temporary identity %s", principal);
/* If there's no account for this identity then create a temporary one.
*/
principal_for_display = goa_identity_manager_name_identity (self->priv->identity_manager,
identity);
realm = goa_kerberos_identity_get_realm_name (GOA_KERBEROS_IDENTITY (identity));
+ preauth_source = goa_kerberos_identity_get_preauthentication_source (GOA_KERBEROS_IDENTITY (identity));
g_variant_builder_init (&credentials, G_VARIANT_TYPE_VARDICT);
g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
g_variant_builder_add (&details, "{ss}", "Realm", realm);
g_variant_builder_add (&details, "{ss}", "IsTemporary", "true");
+ if (preauth_source != NULL)
+ g_variant_builder_add (&details, "{ss}", "PreauthenticationSource", preauth_source);
g_variant_builder_add (&details, "{ss}", "TicketingEnabled", "true");
goa_debug ("GoaIdentityService: asking to sign back in");
operation_result = g_simple_async_result_new (G_OBJECT (self),
(GAsyncReadyCallback)
on_temporary_account_created_for_identity,
identity,
add_temporary_account);
g_hash_table_insert (self->priv->pending_temporary_account_results,
g_strdup (principal),
g_object_ref (operation_result));
goa_manager_call_add_account (self->priv->accounts_manager,
"kerberos",
principal,
principal_for_display,
g_variant_builder_end (&credentials),
g_variant_builder_end (&details),
NULL,
(GAsyncReadyCallback)
on_account_added,
operation_result);
g_free (realm);
+ g_free (preauth_source);
g_free (principal_for_display);
}
static void
on_identity_added (GoaIdentityManager *identity_manager,
GoaIdentity *identity,
GoaIdentityService *self)
{
GoaObject *object;
const char *identifier;
export_identity (self, identity);
identifier = goa_identity_get_identifier (identity);
object = find_object_with_principal (self, identifier, FALSE);
if (object == NULL)
add_temporary_account (self, identity);
}
static void
on_identity_removed (GoaIdentityManager *identity_manager,
GoaIdentity *identity,
GoaIdentityService *self)
{
GoaObject *object;
const char *identifier;
identifier = goa_identity_get_identifier (identity);
@@ -1239,87 +1252,89 @@ on_identity_inquiry (GoaIdentityInquiry *inquiry,
request = system_prompt_open_request_new (self, inquiry, cancellable);
gcr_system_prompt_open_async (-1,
cancellable,
(GAsyncReadyCallback)
on_system_prompt_open,
request);
}
static void
cancel_sign_in (GoaIdentityManager *identity_manager,
GoaIdentity *identity,
GSimpleAsyncResult *operation_result)
{
GoaIdentity *operation_identity;
operation_identity = g_simple_async_result_get_source_tag (operation_result);
if (operation_identity == identity)
{
GCancellable *cancellable;
cancellable = g_object_get_data (G_OBJECT (operation_result),
"cancellable");
g_cancellable_cancel (cancellable);
}
}
static void
sign_in (GoaIdentityService *self,
const char *identifier,
gconstpointer initial_password,
+ const char *preauth_source,
GoaIdentitySignInFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *operation_result;
goa_debug ("GoaIdentityService: asking to sign in");
operation_result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
NULL);
g_simple_async_result_set_check_cancellable (operation_result, cancellable);
g_object_set_data (G_OBJECT (operation_result),
"cancellable",
cancellable);
g_signal_connect_object (G_OBJECT (self->priv->identity_manager),
"identity-refreshed",
G_CALLBACK (cancel_sign_in),
operation_result,
0);
goa_identity_manager_sign_identity_in (self->priv->identity_manager,
identifier,
initial_password,
+ preauth_source,
flags,
(GoaIdentityInquiryFunc)
on_identity_inquiry,
self,
cancellable,
(GAsyncReadyCallback)
on_identity_signed_in,
operation_result);
}
static void
on_identity_expiring (GoaIdentityManager *identity_manager,
GoaIdentity *identity,
GoaIdentityService *self)
{
const char *principal;
GoaObject *object;
principal = goa_identity_get_identifier (identity);
goa_debug ("GoaIdentityService: identity %s expiring", principal);
object = find_object_with_principal (self, principal, TRUE);
if (object == NULL)
return;
ensure_account_credentials (self, object);
}
diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c
index c3a65f6..7641102 100644
--- a/src/goaidentity/goakerberosidentity.c
+++ b/src/goaidentity/goakerberosidentity.c
@@ -25,60 +25,62 @@
#include "goaidentity.h"
#include "goakerberosidentity.h"
#include "goakerberosidentityinquiry.h"
#include "goaalarm.h"
#include "goalogging.h"
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <string.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
typedef enum
{
VERIFICATION_LEVEL_UNVERIFIED,
VERIFICATION_LEVEL_ERROR,
VERIFICATION_LEVEL_EXISTS,
VERIFICATION_LEVEL_SIGNED_IN
} VerificationLevel;
struct _GoaKerberosIdentityPrivate
{
krb5_context kerberos_context;
krb5_ccache credentials_cache;
char *identifier;
guint identifier_idle_id;
+ char *preauth_identity_source;
+
krb5_timestamp expiration_time;
guint expiration_time_idle_id;
GoaAlarm *expiration_alarm;
GoaAlarm *expiring_alarm;
GoaAlarm *renewal_alarm;
VerificationLevel cached_verification_level;
guint is_signed_in_idle_id;
};
enum
{
EXPIRING,
EXPIRED,
UNEXPIRED,
NEEDS_RENEWAL,
NEEDS_REFRESH,
NUMBER_OF_SIGNALS,
};
enum
{
PROP_0,
PROP_IDENTIFIER,
PROP_IS_SIGNED_IN,
PROP_EXPIRATION_TIMESTAMP
};
static guint signals[NUMBER_OF_SIGNALS] = { 0 };
@@ -86,60 +88,62 @@ static guint signals[NUMBER_OF_SIGNALS] = { 0 };
static void identity_interface_init (GoaIdentityInterface *interface);
static void initable_interface_init (GInitableIface *interface);
static void reset_alarms (GoaKerberosIdentity *self);
static void clear_alarms (GoaKerberosIdentity *self);
static gboolean goa_kerberos_identity_is_signed_in (GoaIdentity *identity);
static void set_error_from_krb5_error_code (GoaKerberosIdentity *self,
GError **error,
gint code,
krb5_error_code error_code,
const char *format,
...);
G_LOCK_DEFINE_STATIC (identity_lock);
G_DEFINE_TYPE_WITH_CODE (GoaKerberosIdentity,
goa_kerberos_identity,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
initable_interface_init)
G_IMPLEMENT_INTERFACE (GOA_TYPE_IDENTITY,
identity_interface_init));
static void
goa_kerberos_identity_dispose (GObject *object)
{
GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (object);
G_LOCK (identity_lock);
g_clear_object (&self->priv->renewal_alarm);
g_clear_object (&self->priv->expiring_alarm);
g_clear_object (&self->priv->expiration_alarm);
+ g_clear_pointer (&self->priv->preauth_identity_source,
+ g_free);
G_UNLOCK (identity_lock);
G_OBJECT_CLASS (goa_kerberos_identity_parent_class)->dispose (object);
}
static void
goa_kerberos_identity_finalize (GObject *object)
{
GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (object);
g_free (self->priv->identifier);
if (self->priv->credentials_cache != NULL)
krb5_cc_close (self->priv->kerberos_context, self->priv->credentials_cache);
G_OBJECT_CLASS (goa_kerberos_identity_parent_class)->finalize (object);
}
static void
goa_kerberos_identity_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *param_spec)
{
GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (object);
switch (property_id)
{
case PROP_IDENTIFIER:
@@ -385,108 +389,152 @@ goa_kerberos_identity_get_realm_name (GoaKerberosIdentity *self)
krb5_principal principal;
krb5_error_code error_code;
krb5_data *realm;
char *realm_name;
if (self->priv->identifier == NULL)
return NULL;
error_code = krb5_parse_name (self->priv->kerberos_context,
self->priv->identifier, &principal);
if (error_code != 0)
{
const char *error_message;
error_message =
krb5_get_error_message (self->priv->kerberos_context, error_code);
goa_debug
("GoaKerberosIdentity: Error parsing identity %s into kerberos principal: %s",
self->priv->identifier, error_message);
krb5_free_error_message (self->priv->kerberos_context, error_message);
return NULL;
}
realm = krb5_princ_realm (self->priv->kerberos_context, principal);
realm_name = g_strndup (realm->data, realm->length);
krb5_free_principal (self->priv->kerberos_context, principal);
return realm_name;
}
+char *
+goa_kerberos_identity_get_preauthentication_source (GoaKerberosIdentity *self)
+{
+ return g_strdup (self->priv->preauth_identity_source);
+}
+
static const char *
goa_kerberos_identity_get_identifier (GoaIdentity *identity)
{
GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (identity);
return self->priv->identifier;
}
static gboolean
credentials_validate_existence (GoaKerberosIdentity *self,
krb5_principal principal, krb5_creds * credentials)
{
/* Checks if default principal associated with the cache has a valid
* ticket granting ticket in the passed in credentials
*/
if (krb5_is_config_principal (self->priv->kerberos_context, credentials->server))
return FALSE;
/* looking for the krbtgt / REALM pair, so it should be exactly 2 items */
if (krb5_princ_size (self->priv->kerberos_context, credentials->server) != 2)
return FALSE;
if (!krb5_realm_compare (self->priv->kerberos_context,
credentials->server, principal))
{
/* credentials are from some other realm */
return FALSE;
}
if (strncmp (credentials->server->data[0].data,
KRB5_TGS_NAME, credentials->server->data[0].length) != 0)
{
/* credentials aren't for ticket granting */
return FALSE;
}
if (credentials->server->data[1].length != principal->realm.length ||
memcmp (credentials->server->data[1].data,
principal->realm.data, principal->realm.length) != 0)
{
/* credentials are for some other realm */
return FALSE;
}
return TRUE;
}
+static gboolean
+snoop_preauth_identity_from_credentials (GoaKerberosIdentity *self,
+ krb5_creds *credentials,
+ char **identity_source)
+{
+ GRegex *regex;
+ GMatchInfo *match_info = NULL;
+ gboolean identity_source_exposed = FALSE;
+
+ if (!krb5_is_config_principal (self->priv->kerberos_context, credentials->server))
+ return FALSE;
+
+ regex = g_regex_new ("\"X509_user_identity\":\"(?P<identity_source>[^\"]*)\"",
+ G_REGEX_MULTILINE | G_REGEX_CASELESS | G_REGEX_RAW,
+ 0,
+ NULL);
+
+ if (regex == NULL)
+ return FALSE;
+
+ g_regex_match_full (regex, credentials->ticket.data, credentials->ticket.length, 0, 0, &match_info, NULL);
+
+ if (match_info != NULL && g_match_info_matches (match_info))
+ {
+ if (identity_source)
+ {
+ g_free (*identity_source);
+ *identity_source = g_match_info_fetch_named (match_info, "identity_source");
+ }
+ identity_source_exposed = TRUE;
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (regex);
+
+ return identity_source_exposed;
+}
+
static krb5_timestamp
get_current_time (GoaKerberosIdentity *self)
{
krb5_timestamp current_time;
krb5_error_code error_code;
error_code = krb5_timeofday (self->priv->kerberos_context, ¤t_time);
if (error_code != 0)
{
const char *error_message;
error_message =
krb5_get_error_message (self->priv->kerberos_context, error_code);
goa_debug ("GoaKerberosIdentity: Error getting current time: %s", error_message);
krb5_free_error_message (self->priv->kerberos_context, error_message);
return 0;
}
return current_time;
}
typedef struct
{
GoaKerberosIdentity *self;
guint *idle_id;
const char *property_name;
} NotifyRequest;
static void
@@ -537,60 +585,61 @@ set_expiration_time (GoaKerberosIdentity *self,
if (self->priv->expiration_time != expiration_time)
{
self->priv->expiration_time = expiration_time;
queue_notify (self,
&self->priv->expiration_time_idle_id,
"expiration-timestamp");
}
}
static gboolean
credentials_are_expired (GoaKerberosIdentity *self,
krb5_creds *credentials)
{
krb5_timestamp current_time;
current_time = get_current_time (self);
set_expiration_time (self, MAX (credentials->times.endtime,
self->priv->expiration_time));
if (credentials->times.endtime <= current_time)
{
return TRUE;
}
return FALSE;
}
static VerificationLevel
verify_identity (GoaKerberosIdentity *self,
+ char **preauth_identity_source,
GError **error)
{
krb5_principal principal;
krb5_cc_cursor cursor;
krb5_creds credentials;
krb5_error_code error_code;
VerificationLevel verification_level;
set_expiration_time (self, 0);
if (self->priv->credentials_cache == NULL)
return VERIFICATION_LEVEL_UNVERIFIED;
error_code = krb5_cc_get_principal (self->priv->kerberos_context,
self->priv->credentials_cache,
&principal);
if (error_code != 0)
{
if (error_code == KRB5_CC_END || error_code == KRB5_FCC_NOFILE)
return VERIFICATION_LEVEL_UNVERIFIED;
set_error_from_krb5_error_code (self,
error,
GOA_IDENTITY_ERROR_NOT_FOUND,
error_code,
_("Could not find identity in "
"credential cache: %k"));
return VERIFICATION_LEVEL_ERROR;
}
@@ -599,60 +648,64 @@ verify_identity (GoaKerberosIdentity *self,
self->priv->credentials_cache, &cursor);
if (error_code != 0)
{
set_error_from_krb5_error_code (self,
error,
GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE,
error_code,
_("Could not find identity "
"credentials in cache: %k"));
verification_level = VERIFICATION_LEVEL_ERROR;
goto out;
}
verification_level = VERIFICATION_LEVEL_UNVERIFIED;
error_code = krb5_cc_next_cred (self->priv->kerberos_context,
self->priv->credentials_cache,
&cursor,
&credentials);
while (error_code == 0)
{
if (credentials_validate_existence (self, principal, &credentials))
{
if (!credentials_are_expired (self, &credentials))
verification_level = VERIFICATION_LEVEL_SIGNED_IN;
else
verification_level = VERIFICATION_LEVEL_EXISTS;
}
+ else
+ {
+ snoop_preauth_identity_from_credentials (self, &credentials, preauth_identity_source);
+ }
krb5_free_cred_contents (self->priv->kerberos_context, &credentials);
error_code = krb5_cc_next_cred (self->priv->kerberos_context,
self->priv->credentials_cache,
&cursor,
&credentials);
}
if (error_code != KRB5_CC_END)
{
verification_level = VERIFICATION_LEVEL_ERROR;
set_error_from_krb5_error_code (self,
error,
GOA_IDENTITY_ERROR_ENUMERATING_CREDENTIALS,
error_code,
_("Could not sift through identity "
"credentials in cache: %k"));
goto end_sequence;
}
end_sequence:
error_code = krb5_cc_end_seq_get (self->priv->kerberos_context,
self->priv->credentials_cache,
&cursor);
if (error_code != 0)
{
verification_level = VERIFICATION_LEVEL_ERROR;
@@ -900,61 +953,61 @@ reset_alarms (GoaKerberosIdentity *self)
static void
clear_alarms (GoaKerberosIdentity *self)
{
disconnect_alarm_signals (self);
clear_alarm_and_unref_on_idle (self, &self->priv->renewal_alarm);
clear_alarm_and_unref_on_idle (self, &self->priv->expiring_alarm);
clear_alarm_and_unref_on_idle (self, &self->priv->expiration_alarm);
}
static gboolean
goa_kerberos_identity_initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (initable);
GError *verification_error;
if (g_cancellable_set_error_if_cancelled (cancellable, error))
return FALSE;
if (self->priv->identifier == NULL)
{
self->priv->identifier = get_identifier (self, error);
if (self->priv->identifier != NULL)
queue_notify (self, &self->priv->identifier_idle_id, "identifier");
}
verification_error = NULL;
self->priv->cached_verification_level =
- verify_identity (self, &verification_error);
+ verify_identity (self, &self->priv->preauth_identity_source, &verification_error);
switch (self->priv->cached_verification_level)
{
case VERIFICATION_LEVEL_EXISTS:
case VERIFICATION_LEVEL_SIGNED_IN:
reset_alarms (self);
queue_notify (self, &self->priv->is_signed_in_idle_id, "is-signed-in");
return TRUE;
case VERIFICATION_LEVEL_UNVERIFIED:
return TRUE;
case VERIFICATION_LEVEL_ERROR:
if (verification_error != NULL)
{
g_propagate_error (error, verification_error);
return FALSE;
}
default:
g_set_error (error,
GOA_IDENTITY_ERROR,
GOA_IDENTITY_ERROR_VERIFYING,
_("No associated identification found"));
return FALSE;
}
}
static void
@@ -1105,60 +1158,61 @@ sign_in_operation_new (GoaKerberosIdentity *identity,
{
SignInOperation *operation;
operation = g_slice_new0 (SignInOperation);
operation->identity = g_object_ref (identity);
operation->inquiry_func = inquiry_func;
operation->inquiry_data = inquiry_data;
operation->destroy_notify = destroy_notify;
if (cancellable == NULL)
operation->cancellable = g_cancellable_new ();
else
operation->cancellable = g_object_ref (cancellable);
return operation;
}
static void
sign_in_operation_free (SignInOperation *operation)
{
g_object_unref (operation->identity);
g_object_unref (operation->cancellable);
g_slice_free (SignInOperation, operation);
}
gboolean
goa_kerberos_identity_sign_in (GoaKerberosIdentity *self,
const char *principal_name,
gconstpointer initial_password,
+ const char *preauth_source,
GoaIdentitySignInFlags flags,
GoaIdentityInquiryFunc inquiry_func,
gpointer inquiry_data,
GDestroyNotify destroy_notify,
GCancellable *cancellable,
GError **error)
{
SignInOperation *operation;
krb5_principal principal;
krb5_error_code error_code;
krb5_creds new_credentials;
krb5_get_init_creds_opt *options;
krb5_deltat start_time;
char *service_name;
gboolean signed_in;
if (g_cancellable_set_error_if_cancelled (cancellable, error))
return FALSE;
error_code = krb5_get_init_creds_opt_alloc (self->priv->kerberos_context,
&options);
if (error_code != 0)
{
set_error_from_krb5_error_code (self,
error,
GOA_IDENTITY_ERROR_ALLOCATING_CREDENTIALS,
error_code, "%k");
if (destroy_notify)
destroy_notify (inquiry_data);
return FALSE;
@@ -1176,60 +1230,67 @@ goa_kerberos_identity_sign_in (GoaKerberosIdentity *self,
{
g_free (self->priv->identifier);
self->priv->identifier = g_strdup (principal_name);
}
error_code = krb5_parse_name (self->priv->kerberos_context,
principal_name,
&principal);
if (error_code != 0)
{
set_error_from_krb5_error_code (self,
error,
GOA_IDENTITY_ERROR_PARSING_IDENTIFIER,
error_code,
"%k");
if (destroy_notify)
destroy_notify (inquiry_data);
return FALSE;
}
if ((flags & GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_FORWARDING) == 0)
krb5_get_init_creds_opt_set_forwardable (options, TRUE);
if ((flags & GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_PROXYING) == 0)
krb5_get_init_creds_opt_set_proxiable (options, TRUE);
if ((flags & GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_RENEWAL) == 0)
krb5_get_init_creds_opt_set_renew_life (options, G_MAXINT);
+ if (preauth_source != NULL)
+ {
+ krb5_get_init_creds_opt_set_pa (self->priv->kerberos_context,
+ options,
+ "X509_user_identity", preauth_source);
+ }
+
/* Poke glibc in case the network changed
*/
res_init ();
start_time = 0;
service_name = NULL;
error_code = krb5_get_init_creds_password (self->priv->kerberos_context,
&new_credentials,
principal,
(char *)
initial_password,
(krb5_prompter_fct)
on_kerberos_inquiry,
operation,
start_time,
service_name,
options);
if (error_code == KRB5_LIBOS_PWDINTR)
g_cancellable_cancel (operation->cancellable);
if (g_cancellable_set_error_if_cancelled (cancellable, error))
{
if (destroy_notify)
destroy_notify (inquiry_data);
sign_in_operation_free (operation);
krb5_free_principal (self->priv->kerberos_context, principal);
goto done;
}
@@ -1266,79 +1327,85 @@ goa_kerberos_identity_sign_in (GoaKerberosIdentity *self,
goa_debug ("GoaKerberosIdentity: identity signed in");
signed_in = TRUE;
done:
return signed_in;
}
static void
update_identifier (GoaKerberosIdentity *self, GoaKerberosIdentity *new_identity)
{
char *new_identifier;
new_identifier = get_identifier (self, NULL);
if (g_strcmp0 (self->priv->identifier, new_identifier) != 0)
{
g_free (self->priv->identifier);
self->priv->identifier = new_identifier;
queue_notify (self, &self->priv->identifier_idle_id, "identifier");
}
else
{
g_free (new_identifier);
}
}
void
goa_kerberos_identity_update (GoaKerberosIdentity *self,
GoaKerberosIdentity *new_identity)
{
VerificationLevel verification_level;
+ char *preauth_identity_source = NULL;
if (self->priv->credentials_cache != NULL)
krb5_cc_close (self->priv->kerberos_context, self->priv->credentials_cache);
krb5_cc_dup (new_identity->priv->kerberos_context,
new_identity->priv->credentials_cache,
&self->priv->credentials_cache);
G_LOCK (identity_lock);
update_identifier (self, new_identity);
G_UNLOCK (identity_lock);
- verification_level = verify_identity (self, NULL);
+ verification_level = verify_identity (self, &preauth_identity_source, NULL);
if (verification_level == VERIFICATION_LEVEL_SIGNED_IN)
reset_alarms (self);
else
clear_alarms (self);
+ G_LOCK (identity_lock);
+ g_free (self->priv->preauth_identity_source);
+ self->priv->preauth_identity_source = preauth_identity_source;
+ G_UNLOCK (identity_lock);
+
if (verification_level != self->priv->cached_verification_level)
{
if (self->priv->cached_verification_level == VERIFICATION_LEVEL_SIGNED_IN &&
verification_level == VERIFICATION_LEVEL_EXISTS)
{
G_LOCK (identity_lock);
self->priv->cached_verification_level = verification_level;
G_UNLOCK (identity_lock);
g_signal_emit (G_OBJECT (self), signals[EXPIRED], 0);
}
else if (self->priv->cached_verification_level == VERIFICATION_LEVEL_EXISTS &&
verification_level == VERIFICATION_LEVEL_SIGNED_IN)
{
G_LOCK (identity_lock);
self->priv->cached_verification_level = verification_level;
G_UNLOCK (identity_lock);
g_signal_emit (G_OBJECT (self), signals[UNEXPIRED], 0);
}
else
{
G_LOCK (identity_lock);
self->priv->cached_verification_level = verification_level;
G_UNLOCK (identity_lock);
}
queue_notify (self, &self->priv->is_signed_in_idle_id, "is-signed-in");
}
diff --git a/src/goaidentity/goakerberosidentity.h b/src/goaidentity/goakerberosidentity.h
index 6e82835..e163016 100644
--- a/src/goaidentity/goakerberosidentity.h
+++ b/src/goaidentity/goakerberosidentity.h
@@ -43,47 +43,49 @@ typedef enum _GoaKerberosIdentityDescriptionLevel
GoaKerberosIdentityDescriptionLevel;
enum _GoaKerberosIdentityDescriptionLevel
{
GOA_KERBEROS_IDENTITY_DESCRIPTION_REALM,
GOA_KERBEROS_IDENTITY_DESCRIPTION_USERNAME_AND_REALM,
GOA_KERBEROS_IDENTITY_DESCRIPTION_USERNAME_ROLE_AND_REALM
};
struct _GoaKerberosIdentity
{
GObject parent;
GoaKerberosIdentityPrivate *priv;
};
struct _GoaKerberosIdentityClass
{
GObjectClass parent_class;
};
GType goa_kerberos_identity_get_type (void);
GoaIdentity *goa_kerberos_identity_new (krb5_context kerberos_context,
krb5_ccache cache,
GError **error);
gboolean goa_kerberos_identity_sign_in (GoaKerberosIdentity *self,
const char *principal_name,
gconstpointer initial_password,
+ const char *preauth_source,
GoaIdentitySignInFlags flags,
GoaIdentityInquiryFunc inquiry_func,
gpointer inquiry_data,
GDestroyNotify destroy_notify,
GCancellable *cancellable,
GError **error);
void goa_kerberos_identity_update (GoaKerberosIdentity *identity,
GoaKerberosIdentity *new_identity);
gboolean goa_kerberos_identity_renew (GoaKerberosIdentity *self,
GError **error);
gboolean goa_kerberos_identity_erase (GoaKerberosIdentity *self,
GError **error);
char *goa_kerberos_identity_get_principal_name (GoaKerberosIdentity *self);
char *goa_kerberos_identity_get_realm_name (GoaKerberosIdentity *self);
+char *goa_kerberos_identity_get_preauthentication_source (GoaKerberosIdentity *self);
G_END_DECLS
#endif /* __GOA_KERBEROS_IDENTITY_H__ */
diff --git a/src/goaidentity/goakerberosidentitymanager.c b/src/goaidentity/goakerberosidentitymanager.c
index c4da420..1fa0807 100644
--- a/src/goaidentity/goakerberosidentitymanager.c
+++ b/src/goaidentity/goakerberosidentitymanager.c
@@ -59,60 +59,61 @@ struct _GoaKerberosIdentityManagerPrivate
volatile int pending_refresh_count;
guint polling_timeout_id;
};
typedef enum
{
OPERATION_TYPE_REFRESH,
OPERATION_TYPE_GET_IDENTITY,
OPERATION_TYPE_LIST,
OPERATION_TYPE_RENEW,
OPERATION_TYPE_SIGN_IN,
OPERATION_TYPE_SIGN_OUT,
OPERATION_TYPE_STOP_JOB
} OperationType;
typedef struct
{
GCancellable *cancellable;
GoaKerberosIdentityManager *manager;
OperationType type;
GSimpleAsyncResult *result;
GIOSchedulerJob *job;
union
{
GoaIdentity *identity;
struct
{
const char *identifier;
gconstpointer initial_password;
+ char *preauth_source;
GoaIdentitySignInFlags sign_in_flags;
GoaIdentityInquiry *inquiry;
GoaIdentityInquiryFunc inquiry_func;
gpointer inquiry_data;
GMutex inquiry_lock;
GCond inquiry_finished_condition;
volatile gboolean is_inquiring;
};
};
} Operation;
typedef struct
{
GoaKerberosIdentityManager *manager;
GoaIdentity *identity;
} IdentitySignalWork;
static GoaIdentityManager *goa_kerberos_identity_manager_singleton;
static void identity_manager_interface_init (GoaIdentityManagerInterface *
interface);
static void initable_interface_init (GInitableIface *interface);
static void on_identity_expired (GoaIdentity *identity,
GoaKerberosIdentityManager *self);
G_DEFINE_TYPE_WITH_CODE (GoaKerberosIdentityManager,
goa_kerberos_identity_manager,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GOA_TYPE_IDENTITY_MANAGER,
@@ -129,64 +130,68 @@ operation_new (GoaKerberosIdentityManager *self,
{
Operation *operation;
operation = g_slice_new0 (Operation);
operation->manager = self;
operation->type = type;
if (cancellable == NULL)
cancellable = g_cancellable_new ();
else
g_object_ref (cancellable);
operation->cancellable = cancellable;
if (result != NULL)
g_object_ref (result);
operation->result = result;
operation->identity = NULL;
return operation;
}
static void
operation_free (Operation *operation)
{
g_clear_object (&operation->cancellable);
if (operation->type != OPERATION_TYPE_SIGN_IN &&
operation->type != OPERATION_TYPE_GET_IDENTITY)
- g_clear_object (&operation->identity);
+ {
+ g_clear_object (&operation->identity);
+ }
else
- g_clear_pointer (&operation->identifier, g_free);
-
+ {
+ g_clear_pointer (&operation->identifier, g_free);
+ g_clear_pointer (&operation->preauth_source, g_free);
+ }
g_clear_object (&operation->result);
g_slice_free (Operation, operation);
}
static void
schedule_refresh (GoaKerberosIdentityManager *self)
{
Operation *operation;
g_atomic_int_inc (&self->priv->pending_refresh_count);
operation = operation_new (self, NULL, OPERATION_TYPE_REFRESH, NULL);
g_async_queue_push (self->priv->pending_operations, operation);
}
static IdentitySignalWork *
identity_signal_work_new (GoaKerberosIdentityManager *self,
GoaIdentity *identity)
{
IdentitySignalWork *work;
work = g_slice_new (IdentitySignalWork);
work->manager = self;
work->identity = g_object_ref (identity);
return work;
}
static void
@@ -841,60 +846,61 @@ sign_in_identity (GoaKerberosIdentityManager *self,
_("Could not create credential cache for identity"));
g_simple_async_result_set_op_res_gpointer (operation->result, NULL, NULL);
return;
}
identity = goa_kerberos_identity_new (self->priv->kerberos_context,
credentials_cache,
&error);
krb5_cc_close (self->priv->kerberos_context, credentials_cache);
if (identity == NULL)
{
g_simple_async_result_take_error (operation->result, error);
g_simple_async_result_set_op_res_gpointer (operation->result,
NULL,
NULL);
return;
}
}
else
{
g_object_ref (identity);
}
g_hash_table_replace (self->priv->identities,
g_strdup (operation->identifier),
g_object_ref (identity));
if (!goa_kerberos_identity_sign_in (GOA_KERBEROS_IDENTITY (identity),
operation->identifier,
operation->initial_password,
+ operation->preauth_source,
operation->sign_in_flags,
(GoaIdentityInquiryFunc)
on_kerberos_identity_inquiry,
operation,
NULL,
operation->cancellable,
&error))
{
g_simple_async_result_set_from_error (operation->result, error);
g_simple_async_result_set_op_res_gpointer (operation->result,
NULL,
NULL);
}
else
{
g_simple_async_result_set_op_res_gpointer (operation->result,
g_object_ref (identity),
(GDestroyNotify)
g_object_unref);
}
g_object_unref (identity);
}
static void
sign_out_identity (GoaKerberosIdentityManager *self,
Operation *operation)
{
GError *error;
@@ -1156,83 +1162,85 @@ goa_kerberos_identity_manager_renew_identity (GoaIdentityManager *manager,
GoaKerberosIdentityManager *self = GOA_KERBEROS_IDENTITY_MANAGER (manager);
GSimpleAsyncResult *result;
Operation *operation;
result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
goa_kerberos_identity_manager_renew_identity);
operation = operation_new (self, cancellable, OPERATION_TYPE_RENEW, result);
g_object_unref (result);
operation->identity = g_object_ref (identity);
g_async_queue_push (self->priv->pending_operations, operation);
}
static void
goa_kerberos_identity_manager_renew_identity_finish (GoaIdentityManager *self,
GAsyncResult *result,
GError **error)
{
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
error))
return;
}
static void
goa_kerberos_identity_manager_sign_identity_in (GoaIdentityManager *manager,
const char *identifier,
gconstpointer initial_password,
+ const char *preauth_source,
GoaIdentitySignInFlags flags,
GoaIdentityInquiryFunc inquiry_func,
gpointer inquiry_data,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GoaKerberosIdentityManager *self = GOA_KERBEROS_IDENTITY_MANAGER (manager);
GSimpleAsyncResult *result;
Operation *operation;
result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
goa_kerberos_identity_manager_sign_identity_in);
operation = operation_new (self, cancellable, OPERATION_TYPE_SIGN_IN, result);
g_object_unref (result);
operation->identifier = g_strdup (identifier);
/* Not duped. Caller is responsible for ensuring it stays alive
* for duration of operation
*/
operation->initial_password = initial_password;
+ operation->preauth_source = g_strdup (preauth_source);
operation->sign_in_flags = flags;
operation->inquiry_func = inquiry_func;
operation->inquiry_data = inquiry_data;
g_mutex_init (&operation->inquiry_lock);
g_cond_init (&operation->inquiry_finished_condition);
operation->is_inquiring = FALSE;
g_async_queue_push (self->priv->pending_operations, operation);
}
static GoaIdentity *
goa_kerberos_identity_manager_sign_identity_in_finish (GoaIdentityManager *self,
GAsyncResult *result,
GError **error)
{
GoaIdentity *identity;
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
return NULL;
identity =
g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
return identity;
}
static void
goa_kerberos_identity_manager_sign_identity_out (GoaIdentityManager *manager,
GoaIdentity *identity,
GCancellable *cancellable,
--
1.8.3.1