Line data Source code
1 : /*
2 : * gnome-keyring
3 : *
4 : * Copyright (C) 2018 Red Hat, Inc.
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU Lesser General Public License as
8 : * published by the Free Software Foundation; either version 2.1 of
9 : * the License, or (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful, but
12 : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : * Lesser General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public
17 : * License along with this program; if not, see
18 : * <http://www.gnu.org/licenses/>.
19 : *
20 : * Author: Daiki Ueno
21 : */
22 :
23 : #include "config.h"
24 :
25 : #include "gkd-login-interaction.h"
26 : #include "gkd-login-password.h"
27 :
28 : #include <gcr/gcr-unlock-options.h>
29 : #include "gkd-login.h"
30 :
31 : #include "egg/egg-secure-memory.h"
32 : #include <glib/gi18n.h>
33 : #include <string.h>
34 :
35 : static const gchar *XDG_SCHEMA = "xdg:schema";
36 : static const gchar *GENERIC_SCHEMA_VALUE = "org.freedesktop.Secret.Generic";
37 :
38 : enum {
39 : PROP_0,
40 : PROP_BASE,
41 : PROP_SESSION,
42 : PROP_LABEL,
43 : PROP_FIELDS
44 : };
45 :
46 : struct _GkdLoginInteraction
47 : {
48 : GTlsInteraction interaction;
49 :
50 : GTlsInteraction *base;
51 : GckSession *session;
52 : gchar *label;
53 : GHashTable *lookup_fields;
54 : GHashTable *store_fields;
55 : gboolean login_available;
56 : gboolean login_checked;
57 : };
58 :
59 0 : G_DEFINE_TYPE (GkdLoginInteraction, gkd_login_interaction, G_TYPE_TLS_INTERACTION);
60 :
61 0 : EGG_SECURE_DECLARE (gkd_login_interaction);
62 :
63 : static void
64 0 : gkd_login_interaction_init (GkdLoginInteraction *self)
65 : {
66 0 : }
67 :
68 : static void
69 0 : gkd_login_interaction_constructed (GObject *object)
70 : {
71 0 : GkdLoginInteraction *self = GKD_LOGIN_INTERACTION (object);
72 :
73 0 : self->login_available = gkd_login_available (self->session);
74 :
75 0 : if (g_hash_table_contains (self->lookup_fields, (gpointer) XDG_SCHEMA))
76 0 : self->store_fields = g_hash_table_ref (self->lookup_fields);
77 : else {
78 : GHashTableIter iter;
79 : gpointer key, value;
80 :
81 0 : self->store_fields = g_hash_table_new (g_str_hash, g_str_equal);
82 0 : g_hash_table_iter_init (&iter, self->lookup_fields);
83 0 : while (g_hash_table_iter_next (&iter, &key, &value))
84 0 : g_hash_table_insert (self->store_fields, key, value);
85 0 : g_hash_table_insert (self->store_fields, (gpointer) XDG_SCHEMA, (gpointer) GENERIC_SCHEMA_VALUE);
86 : }
87 :
88 0 : G_OBJECT_CLASS (gkd_login_interaction_parent_class)->constructed (object);
89 0 : }
90 :
91 : static GkdLoginPassword *
92 0 : wrap_password (GkdLoginInteraction *self,
93 : GTlsPassword *password)
94 : {
95 : GkdLoginPassword *wrapped;
96 :
97 0 : wrapped = g_object_new (GKD_TYPE_LOGIN_PASSWORD,
98 : "base", password,
99 : "login-available", self->login_available,
100 : NULL);
101 0 : g_tls_password_set_description (G_TLS_PASSWORD (wrapped), self->label);
102 :
103 0 : return wrapped;
104 : }
105 :
106 : static void
107 0 : on_ask_password_ready (GObject *source_object,
108 : GAsyncResult *res,
109 : gpointer user_data)
110 : {
111 0 : GTask *task = G_TASK (user_data);
112 0 : GkdLoginInteraction *self = g_task_get_source_object (task);
113 : GTlsInteractionResult result;
114 0 : GError *error = NULL;
115 :
116 0 : result = g_tls_interaction_ask_password_finish (self->base, res, &error);
117 0 : if (result == G_TLS_INTERACTION_FAILED && error != NULL)
118 0 : g_task_return_error (task, error);
119 : else
120 0 : g_task_return_int (task, result);
121 0 : g_object_unref (task);
122 0 : }
123 :
124 : static void
125 0 : gkd_login_interaction_ask_password_async (GTlsInteraction *interaction,
126 : GTlsPassword *password,
127 : GCancellable *cancellable,
128 : GAsyncReadyCallback callback,
129 : gpointer user_data)
130 : {
131 0 : GkdLoginInteraction *self = GKD_LOGIN_INTERACTION (interaction);
132 : GkdLoginPassword *login_password;
133 : GTask *task;
134 :
135 0 : login_password = wrap_password (self, password);
136 0 : task = g_task_new (interaction, cancellable, callback, user_data);
137 0 : g_task_set_task_data (task, g_object_ref (login_password), g_object_unref);
138 :
139 : /* If the login keyring is available, look for the password there */
140 0 : if (self->login_available) {
141 0 : if (self->login_checked)
142 0 : g_message ("already attempted to use password from login keyring");
143 : else {
144 0 : gchar *value = gkd_login_lookup_passwordv (self->session, self->lookup_fields);
145 0 : self->login_checked = TRUE;
146 0 : if (value) {
147 0 : g_tls_password_set_value_full (G_TLS_PASSWORD (login_password), (guchar *)value, strlen (value), (GDestroyNotify)egg_secure_free);
148 0 : g_object_unref (login_password);
149 0 : g_task_return_int (task, G_TLS_INTERACTION_HANDLED);
150 0 : g_object_unref (task);
151 0 : return;
152 : }
153 : }
154 : }
155 :
156 : /* Otherwise, call out to the base interaction */
157 0 : g_tls_interaction_ask_password_async (self->base,
158 0 : G_TLS_PASSWORD (login_password),
159 : cancellable,
160 : on_ask_password_ready,
161 : task);
162 0 : g_object_unref (login_password);
163 : }
164 :
165 : static GTlsInteractionResult
166 0 : gkd_login_interaction_ask_password_finish (GTlsInteraction *interaction,
167 : GAsyncResult *res,
168 : GError **error)
169 : {
170 0 : GkdLoginInteraction *self = GKD_LOGIN_INTERACTION (interaction);
171 0 : GTask *task = G_TASK (res);
172 0 : GkdLoginPassword *login_password = g_task_get_task_data (task);
173 : GTlsInteractionResult result;
174 :
175 0 : result = g_task_propagate_int (task, error);
176 0 : if (result == -1)
177 0 : result = G_TLS_INTERACTION_FAILED;
178 :
179 0 : if (self->login_available &&
180 0 : result == G_TLS_INTERACTION_HANDLED &&
181 0 : gkd_login_password_get_store_password (login_password)) {
182 : const guchar *value;
183 : gsize length;
184 : gchar *password;
185 : gchar *label;
186 :
187 0 : value = g_tls_password_get_value (G_TLS_PASSWORD (login_password),
188 : &length);
189 :
190 0 : password = egg_secure_strndup ((const gchar *)value, length);
191 0 : label = g_strdup_printf (_("Unlock password for: %s"), self->label);
192 0 : gkd_login_store_passwordv (self->session,
193 : password,
194 : label,
195 : GCR_UNLOCK_OPTION_ALWAYS, -1,
196 : self->store_fields);
197 0 : egg_secure_free (password);
198 0 : g_free (label);
199 : }
200 :
201 0 : return result;
202 : }
203 :
204 : static void
205 0 : gkd_login_interaction_set_property (GObject *object,
206 : guint prop_id,
207 : const GValue *value,
208 : GParamSpec *pspec)
209 : {
210 0 : GkdLoginInteraction *self = GKD_LOGIN_INTERACTION (object);
211 :
212 0 : switch (prop_id)
213 : {
214 0 : case PROP_BASE:
215 0 : self->base = g_value_dup_object (value);
216 0 : break;
217 0 : case PROP_SESSION:
218 0 : self->session = g_value_dup_object (value);
219 0 : break;
220 0 : case PROP_LABEL:
221 0 : self->label = g_value_dup_string (value);
222 0 : break;
223 0 : case PROP_FIELDS:
224 0 : self->lookup_fields = g_value_dup_boxed (value);
225 0 : break;
226 0 : default:
227 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
228 0 : break;
229 : }
230 0 : }
231 :
232 : static void
233 0 : gkd_login_interaction_dispose (GObject *object)
234 : {
235 0 : GkdLoginInteraction *self = GKD_LOGIN_INTERACTION (object);
236 :
237 0 : g_clear_object (&self->base);
238 0 : g_clear_object (&self->session);
239 :
240 0 : G_OBJECT_CLASS (gkd_login_interaction_parent_class)->dispose (object);
241 0 : }
242 :
243 : static void
244 0 : gkd_login_interaction_finalize (GObject *object)
245 : {
246 0 : GkdLoginInteraction *self = GKD_LOGIN_INTERACTION (object);
247 :
248 0 : g_free (self->label);
249 0 : g_hash_table_unref (self->lookup_fields);
250 0 : g_hash_table_unref (self->store_fields);
251 :
252 0 : G_OBJECT_CLASS (gkd_login_interaction_parent_class)->finalize (object);
253 0 : }
254 :
255 : static void
256 0 : gkd_login_interaction_class_init (GkdLoginInteractionClass *klass)
257 : {
258 0 : GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass);
259 0 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
260 :
261 0 : interaction_class->ask_password_async = gkd_login_interaction_ask_password_async;
262 0 : interaction_class->ask_password_finish = gkd_login_interaction_ask_password_finish;
263 :
264 0 : gobject_class->constructed = gkd_login_interaction_constructed;
265 0 : gobject_class->set_property = gkd_login_interaction_set_property;
266 0 : gobject_class->dispose = gkd_login_interaction_dispose;
267 0 : gobject_class->finalize = gkd_login_interaction_finalize;
268 :
269 0 : g_object_class_install_property (gobject_class, PROP_BASE,
270 : g_param_spec_object ("base", "Base", "Base",
271 : G_TYPE_TLS_INTERACTION,
272 : G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
273 0 : g_object_class_install_property (gobject_class, PROP_SESSION,
274 : g_param_spec_object ("session", "Session", "Session",
275 : GCK_TYPE_SESSION,
276 : G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
277 0 : g_object_class_install_property (gobject_class, PROP_LABEL,
278 : g_param_spec_string ("label", "Label", "Label",
279 : "",
280 : G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
281 0 : g_object_class_install_property (gobject_class, PROP_FIELDS,
282 : g_param_spec_boxed ("fields", "Fields", "Fields",
283 : G_TYPE_HASH_TABLE,
284 : G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
285 0 : }
286 :
287 : GTlsInteraction *
288 0 : gkd_login_interaction_new (GTlsInteraction *base,
289 : GckSession *session,
290 : const gchar *label,
291 : GHashTable *fields)
292 : {
293 0 : return g_object_new (GKD_TYPE_LOGIN_INTERACTION,
294 : "base", base,
295 : "session", session,
296 : "label", label,
297 : "fields", fields,
298 : NULL);
299 : }
|