From 3753904f1de123a724438bf7f0c58aac00ce4ef4 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 21 Oct 2014 10:38:17 -0400
Subject: [PATCH 1/2] 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 5bf26e9..fa8b18c 100644
--- a/data/dbus-interfaces.xml
+++ b/data/dbus-interfaces.xml
@@ -726,6 +726,8 @@
ticketing capabilities.
-->
<interface name="org.gnome.OnlineAccounts.Ticketing">
+ <property name="details" type="a{ss}" access="read"/>
+
<!--
GetTicket:
--
2.1.0
From 1544883b52453ba8e69e78b5bdacf7a57053326c Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 21 Oct 2014 10:46:50 -0400
Subject: [PATCH 2/2] kerberos: Support refreshing smartcard authenticated
kerberos tickets
Right now gnome-online-accounts doesn't manage 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.
https://bugzilla.gnome.org/show_bug.cgi?id=739594
---
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 b2958b2..f9c54cd 100644
--- a/src/goabackend/goakerberosprovider.c
+++ b/src/goabackend/goakerberosprovider.c
@@ -370,6 +370,7 @@ on_secret_keys_exchanged_for_sign_in (GoaKerberosProvider *self,
{
const char *identifier;
const char *password;
+ const char *preauth_source;
GCancellable *cancellable;
GError *error;
GVariantBuilder details;
@@ -387,6 +388,7 @@ on_secret_keys_exchanged_for_sign_in (GoaKerberosProvider *self,
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}"));
@@ -403,6 +405,11 @@ on_secret_keys_exchanged_for_sign_in (GoaKerberosProvider *self,
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),
@@ -538,6 +545,7 @@ static void
sign_in_identity (GoaKerberosProvider *self,
const char *identifier,
const char *password,
+ const char *preauth_source,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -560,6 +568,11 @@ sign_in_identity (GoaKerberosProvider *self,
(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)
@@ -691,8 +704,11 @@ get_ticket_sync (GoaKerberosProvider *self,
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;
@@ -700,6 +716,13 @@ get_ticket_sync (GoaKerberosProvider *self,
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;
@@ -742,6 +765,7 @@ get_ticket_sync (GoaKerberosProvider *self,
sign_in_identity (self,
identifier,
password,
+ preauth_source,
cancellable,
(GAsyncReadyCallback)
on_account_signed_in,
@@ -758,6 +782,8 @@ get_ticket_sync (GoaKerberosProvider *self,
ret = TRUE;
out:
+ g_clear_object (&ticketing);
+
if (credentials != NULL)
g_variant_unref (credentials);
@@ -855,6 +881,9 @@ build_object (GoaProvider *provider,
{
if (ticketing == NULL)
{
+ char *preauthentication_source;
+ GVariantBuilder details;
+
ticketing = goa_ticketing_skeleton_new ();
g_signal_connect (ticketing,
@@ -864,6 +893,13 @@ build_object (GoaProvider *provider,
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)
@@ -1274,12 +1310,18 @@ on_system_prompt_answered_for_initial_sign_in (GcrPrompt *prompt,
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);
@@ -1313,6 +1355,7 @@ on_system_prompt_answered_for_initial_sign_in (GcrPrompt *prompt,
sign_in_identity (self,
principal,
password,
+ preauth_source,
cancellable,
(GAsyncReadyCallback)
on_initial_sign_in_done,
diff --git a/src/goaidentity/goaidentitymanager.c b/src/goaidentity/goaidentitymanager.c
index 40d2225..22dcd97 100644
--- a/src/goaidentity/goaidentitymanager.c
+++ b/src/goaidentity/goaidentitymanager.c
@@ -202,6 +202,7 @@ 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,
@@ -212,6 +213,7 @@ goa_identity_manager_sign_identity_in (GoaIdentityManager *self,
GOA_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_in (self,
identifier,
initial_password,
+ preauth_source,
flags,
inquiry_func,
inquiry_data,
diff --git a/src/goaidentity/goaidentitymanager.h b/src/goaidentity/goaidentitymanager.h
index 89e6b6e..755053a 100644
--- a/src/goaidentity/goaidentitymanager.h
+++ b/src/goaidentity/goaidentitymanager.h
@@ -77,6 +77,7 @@ struct _GoaIdentityManagerInterface
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,
@@ -140,6 +141,7 @@ GList *goa_identity_manager_list_identities_finish (GoaIdentityManager *identit
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,
diff --git a/src/goaidentity/goaidentityservice.c b/src/goaidentity/goaidentityservice.c
index 06fb946..38bbde6 100644
--- a/src/goaidentity/goaidentityservice.c
+++ b/src/goaidentity/goaidentityservice.c
@@ -60,6 +60,7 @@ static void
sign_in (GoaIdentityService *self,
const char *identifier,
gconstpointer initial_password,
+ const char *preauth_source,
GoaIdentitySignInFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
@@ -312,7 +313,8 @@ 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;
@@ -324,6 +326,8 @@ read_sign_in_details (GoaIdentityServiceManager *manager,
{
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;
@@ -346,13 +350,15 @@ goa_identity_service_handle_sign_in (GoaIdentityServiceManager *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)
{
@@ -397,12 +403,14 @@ goa_identity_service_handle_sign_in (GoaIdentityServiceManager *manager,
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;
@@ -868,6 +876,7 @@ add_temporary_account (GoaIdentityService *self,
GoaIdentity *identity)
{
char *realm;
+ char *preauth_source;
const char *principal;
char *principal_for_display;
GSimpleAsyncResult *operation_result;
@@ -894,12 +903,15 @@ add_temporary_account (GoaIdentityService *self,
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");
@@ -925,6 +937,7 @@ add_temporary_account (GoaIdentityService *self,
on_account_added,
operation_result);
g_free (realm);
+ g_free (preauth_source);
g_free (principal_for_display);
}
@@ -1261,6 +1274,7 @@ static void
sign_in (GoaIdentityService *self,
const char *identifier,
gconstpointer initial_password,
+ const char *preauth_source,
GoaIdentitySignInFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
@@ -1288,6 +1302,7 @@ sign_in (GoaIdentityService *self,
goa_identity_manager_sign_identity_in (self->priv->identity_manager,
identifier,
initial_password,
+ preauth_source,
flags,
(GoaIdentityInquiryFunc)
on_identity_inquiry,
diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c
index d501a59..4370a09 100644
--- a/src/goaidentity/goakerberosidentity.c
+++ b/src/goaidentity/goakerberosidentity.c
@@ -47,6 +47,8 @@ struct _GoaKerberosIdentityPrivate
char *identifier;
guint identifier_idle_id;
+ char *preauth_identity_source;
+
krb5_timestamp expiration_time;
guint expiration_time_idle_id;
@@ -106,6 +108,8 @@ goa_kerberos_identity_dispose (GObject *object)
G_LOCK (identity_lock);
clear_alarms (self);
+ g_clear_pointer (&self->priv->preauth_identity_source,
+ g_free);
G_UNLOCK (identity_lock);
G_OBJECT_CLASS (goa_kerberos_identity_parent_class)->dispose (object);
@@ -406,6 +410,12 @@ goa_kerberos_identity_get_realm_name (GoaKerberosIdentity *self)
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)
{
@@ -454,6 +464,44 @@ credentials_validate_existence (GoaKerberosIdentity *self,
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)
{
@@ -565,6 +613,7 @@ credentials_are_expired (GoaKerberosIdentity *self,
static VerificationLevel
verify_identity (GoaKerberosIdentity *self,
+ char **preauth_identity_source,
GError **error)
{
krb5_principal principal = NULL;
@@ -627,6 +676,10 @@ verify_identity (GoaKerberosIdentity *self,
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);
@@ -933,7 +986,7 @@ goa_kerberos_identity_initable_init (GInitable *initable,
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)
{
@@ -1140,6 +1193,7 @@ 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,
@@ -1211,6 +1265,13 @@ goa_kerberos_identity_sign_in (GoaKerberosIdentity *self,
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 ();
@@ -1301,6 +1362,7 @@ 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);
@@ -1313,13 +1375,18 @@ goa_kerberos_identity_update (GoaKerberosIdentity *self,
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 &&
diff --git a/src/goaidentity/goakerberosidentity.h b/src/goaidentity/goakerberosidentity.h
index 1e24796..8d2860a 100644
--- a/src/goaidentity/goakerberosidentity.h
+++ b/src/goaidentity/goakerberosidentity.h
@@ -66,6 +66,7 @@ GoaIdentity *goa_kerberos_identity_new (krb5_context kerberos_context,
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,
@@ -81,5 +82,6 @@ gboolean goa_kerberos_identity_erase (GoaKerberosIdentity *self,
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 c9796ad..a1898c9 100644
--- a/src/goaidentity/goakerberosidentitymanager.c
+++ b/src/goaidentity/goakerberosidentitymanager.c
@@ -81,6 +81,7 @@ typedef struct
{
const char *identifier;
gconstpointer initial_password;
+ char *preauth_source;
GoaIdentitySignInFlags sign_in_flags;
GoaIdentityInquiry *inquiry;
GoaIdentityInquiryFunc inquiry_func;
@@ -151,10 +152,14 @@ operation_free (Operation *operation)
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);
@@ -863,6 +868,7 @@ sign_in_identity (GoaKerberosIdentityManager *self,
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,
@@ -1178,6 +1184,7 @@ 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,
@@ -1201,6 +1208,7 @@ goa_kerberos_identity_manager_sign_identity_in (GoaIdentityManager *manager,
* 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;
--
2.1.0