Blob Blame History Raw
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