Branch data 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 "gcr-ssh-agent-interaction.h"
26 : :
27 : : #include "gcr-ssh-agent-private.h"
28 : : #include "gcr-prompt.h"
29 : : #include "gcr-system-prompt.h"
30 : :
31 : : #include "egg/egg-secure-memory.h"
32 : : #include <glib/gi18n-lib.h>
33 : : #include <libsecret/secret.h>
34 : :
35 : : static const SecretSchema schema = {
36 : : "org.freedesktop.Secret.Generic", SECRET_SCHEMA_NONE,
37 : : {
38 : : { "unique", SECRET_SCHEMA_ATTRIBUTE_STRING },
39 : : { NULL, 0 }
40 : : }
41 : : };
42 : :
43 : : enum {
44 : : PROP_0,
45 : : PROP_PROMPTER_NAME,
46 : : PROP_LABEL,
47 : : PROP_FIELDS
48 : : };
49 : :
50 : : struct _GcrSshAgentInteraction {
51 : : GTlsInteraction interaction;
52 : :
53 : : gchar *prompter_name;
54 : : gchar *label;
55 : : GHashTable *fields;
56 : : };
57 : :
58 [ # # # # : 0 : G_DEFINE_TYPE (GcrSshAgentInteraction, gcr_ssh_agent_interaction, G_TYPE_TLS_INTERACTION);
# # ]
59 : :
60 : 0 : EGG_SECURE_DECLARE (gcr_ssh_agent_interaction);
61 : :
62 : : static void
63 : 0 : gcr_ssh_agent_interaction_init (GcrSshAgentInteraction *self)
64 : : {
65 : 0 : }
66 : :
67 : : static void
68 : 0 : on_store_password_ready (GObject *source_object,
69 : : GAsyncResult *res,
70 : : gpointer user_data)
71 : : {
72 : 0 : GTask *task = G_TASK (user_data);
73 : 0 : GError *error = NULL;
74 : :
75 [ # # ]: 0 : if (secret_password_store_finish (res, &error)) {
76 : 0 : g_task_return_int (task, G_TLS_INTERACTION_HANDLED);
77 : : } else {
78 : 0 : g_task_return_error (task, error);
79 : : }
80 : 0 : g_object_unref (task);
81 : 0 : }
82 : :
83 : : static void
84 : 0 : on_prompt_password (GObject *source_object,
85 : : GAsyncResult *result,
86 : : gpointer user_data)
87 : : {
88 : 0 : GTask *task = G_TASK (user_data);
89 : 0 : GcrSshAgentInteraction *self = g_task_get_source_object (task);
90 : 0 : GTlsPassword *password = G_TLS_PASSWORD (g_task_get_task_data (task));
91 : 0 : GcrPrompt *prompt = GCR_PROMPT (source_object);
92 : 0 : GError *error = NULL;
93 : : const gchar *value;
94 : : gchar *secure_value;
95 : :
96 : 0 : value = gcr_prompt_password_finish (prompt, result, &error);
97 [ # # ]: 0 : if (!value) {
98 : 0 : g_object_unref (prompt);
99 [ # # ]: 0 : if (error)
100 : 0 : g_task_return_error (task, error);
101 : : else
102 : 0 : g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "cancelled");
103 : 0 : g_object_unref (task);
104 : 0 : return;
105 : : }
106 : :
107 : 0 : secure_value = egg_secure_strndup (value, strlen (value));
108 : 0 : g_tls_password_set_value_full (password,
109 : : (guchar *) secure_value,
110 : 0 : strlen (secure_value),
111 : : (GDestroyNotify) egg_secure_free);
112 : :
113 [ # # ]: 0 : if (gcr_prompt_get_choice_chosen (prompt)) {
114 : : gchar *label;
115 : :
116 : 0 : label = g_strdup_printf (_("Unlock password for: %s"), self->label);
117 : 0 : secret_password_storev (&schema,
118 : : self->fields,
119 : : NULL,
120 : : label,
121 : : secure_value,
122 : : g_task_get_cancellable (task),
123 : : on_store_password_ready,
124 : : task);
125 : 0 : g_free (label);
126 : : } else {
127 : 0 : g_task_return_int (task, G_TLS_INTERACTION_HANDLED);
128 : 0 : g_object_unref (task);
129 : : }
130 : 0 : g_object_unref (prompt);
131 : : }
132 : :
133 : : static void
134 : 0 : on_prompt_open (GObject *source_object,
135 : : GAsyncResult *result,
136 : : gpointer user_data)
137 : : {
138 : 0 : GTask *task = G_TASK (user_data);
139 : 0 : GTlsPassword *password = g_task_get_task_data (task);
140 : 0 : GError *error = NULL;
141 : : GcrPrompt *prompt;
142 : : const gchar *choice;
143 : : gchar *text;
144 : :
145 : 0 : prompt = gcr_system_prompt_open_finish (result, &error);
146 [ # # ]: 0 : if (!prompt) {
147 : 0 : g_task_return_error (task, error);
148 : 0 : g_object_unref (task);
149 : 0 : return;
150 : : }
151 : :
152 : 0 : gcr_prompt_set_title (prompt, _("Unlock private key"));
153 : 0 : gcr_prompt_set_message (prompt, _("Enter password to unlock the private key"));
154 : :
155 : : /* TRANSLATORS: The private key is locked */
156 : 0 : text = g_strdup_printf (_("An application wants access to the private key “%s”, but it is locked"),
157 : : g_tls_password_get_description (password));
158 : 0 : gcr_prompt_set_description (prompt, text);
159 : 0 : g_free (text);
160 : :
161 : 0 : choice = _("Automatically unlock this key whenever I’m logged in");
162 : 0 : gcr_prompt_set_choice_label (prompt, choice);
163 : 0 : gcr_prompt_set_continue_label (prompt, _("Unlock"));
164 : :
165 [ # # ]: 0 : if (g_tls_password_get_flags (password) & G_TLS_PASSWORD_RETRY)
166 : 0 : gcr_prompt_set_warning (prompt, _("The unlock password was incorrect"));
167 : :
168 : 0 : gcr_prompt_password_async (prompt, g_task_get_cancellable (task), on_prompt_password, task);
169 : : }
170 : :
171 : : static void
172 : 0 : on_lookup_password_ready (GObject *source_object,
173 : : GAsyncResult *res,
174 : : gpointer user_data)
175 : : {
176 : 0 : GTask *task = G_TASK (user_data);
177 : 0 : GcrSshAgentInteraction *self = g_task_get_source_object (task);
178 : 0 : GTlsPassword *password = G_TLS_PASSWORD (g_task_get_task_data (task));
179 : 0 : GError *error = NULL;
180 : :
181 : 0 : gchar *value = secret_password_lookup_finish (res, &error);
182 [ # # ]: 0 : if (value) {
183 : : /* If password is found in the login keyring, return it */
184 : 0 : g_tls_password_set_value_full (password,
185 : 0 : (guchar *)value, strlen (value),
186 : : (GDestroyNotify)egg_secure_free);
187 : 0 : g_task_return_int (task, G_TLS_INTERACTION_HANDLED);
188 : 0 : g_object_unref (task);
189 : : } else {
190 : : /* Otherwise, prompt a password */
191 : 0 : gcr_system_prompt_open_for_prompter_async (self->prompter_name, 60,
192 : : g_task_get_cancellable (task),
193 : : on_prompt_open,
194 : : task);
195 : : }
196 : 0 : }
197 : :
198 : : static void
199 : 0 : gcr_ssh_agent_interaction_ask_password_async (GTlsInteraction *interaction,
200 : : GTlsPassword *password,
201 : : GCancellable *cancellable,
202 : : GAsyncReadyCallback callback,
203 : : gpointer user_data)
204 : : {
205 : 0 : GcrSshAgentInteraction *self = GCR_SSH_AGENT_INTERACTION (interaction);
206 : : GTask *task;
207 : :
208 : 0 : task = g_task_new (interaction, cancellable, callback, user_data);
209 : 0 : g_task_set_task_data (task, g_object_ref (password), g_object_unref);
210 : :
211 : 0 : secret_password_lookupv (&schema,
212 : : self->fields,
213 : : cancellable,
214 : : on_lookup_password_ready,
215 : : task);
216 : 0 : }
217 : :
218 : : static GTlsInteractionResult
219 : 0 : gcr_ssh_agent_interaction_ask_password_finish (GTlsInteraction *interaction,
220 : : GAsyncResult *res,
221 : : GError **error)
222 : : {
223 : 0 : GTask *task = G_TASK (res);
224 : : GTlsInteractionResult result;
225 : :
226 : 0 : result = g_task_propagate_int (task, error);
227 [ # # ]: 0 : if (result == -1)
228 : 0 : return G_TLS_INTERACTION_FAILED;
229 : 0 : return result;
230 : : }
231 : :
232 : : static void
233 : 0 : gcr_ssh_agent_interaction_set_property (GObject *object,
234 : : guint prop_id,
235 : : const GValue *value,
236 : : GParamSpec *pspec)
237 : : {
238 : 0 : GcrSshAgentInteraction *self = GCR_SSH_AGENT_INTERACTION (object);
239 : :
240 [ # # # # ]: 0 : switch (prop_id) {
241 : 0 : case PROP_PROMPTER_NAME:
242 : 0 : self->prompter_name = g_value_dup_string (value);
243 : 0 : break;
244 : 0 : case PROP_LABEL:
245 : 0 : self->label = g_value_dup_string (value);
246 : 0 : break;
247 : 0 : case PROP_FIELDS:
248 : 0 : self->fields = g_value_dup_boxed (value);
249 : 0 : break;
250 : 0 : default:
251 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
252 : 0 : break;
253 : : }
254 : 0 : }
255 : :
256 : : static void
257 : 0 : gcr_ssh_agent_interaction_finalize (GObject *object)
258 : : {
259 : 0 : GcrSshAgentInteraction *self = GCR_SSH_AGENT_INTERACTION (object);
260 : :
261 : 0 : g_free (self->prompter_name);
262 : 0 : g_free (self->label);
263 : 0 : g_hash_table_unref (self->fields);
264 : :
265 : 0 : G_OBJECT_CLASS (gcr_ssh_agent_interaction_parent_class)->finalize (object);
266 : 0 : }
267 : :
268 : : static void
269 : 0 : gcr_ssh_agent_interaction_class_init (GcrSshAgentInteractionClass *klass)
270 : : {
271 : 0 : GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass);
272 : 0 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
273 : :
274 : 0 : interaction_class->ask_password_async = gcr_ssh_agent_interaction_ask_password_async;
275 : 0 : interaction_class->ask_password_finish = gcr_ssh_agent_interaction_ask_password_finish;
276 : :
277 : 0 : gobject_class->set_property = gcr_ssh_agent_interaction_set_property;
278 : 0 : gobject_class->finalize = gcr_ssh_agent_interaction_finalize;
279 : :
280 : 0 : g_object_class_install_property (gobject_class, PROP_PROMPTER_NAME,
281 : : g_param_spec_string ("prompter-name", "Prompter-name", "Prompter-name",
282 : : NULL,
283 : : G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
284 : 0 : g_object_class_install_property (gobject_class, PROP_LABEL,
285 : : g_param_spec_string ("label", "Label", "Label",
286 : : "",
287 : : G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
288 : 0 : g_object_class_install_property (gobject_class, PROP_FIELDS,
289 : : g_param_spec_boxed ("fields", "Fields", "Fields",
290 : : G_TYPE_HASH_TABLE,
291 : : G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
292 : 0 : }
293 : :
294 : : GTlsInteraction *
295 : 0 : gcr_ssh_agent_interaction_new (const gchar *prompter_name,
296 : : const gchar *label,
297 : : GHashTable *fields)
298 : : {
299 : 0 : return g_object_new (GCR_TYPE_SSH_AGENT_INTERACTION,
300 : : "prompter-name", prompter_name,
301 : : "label", label,
302 : : "fields", fields,
303 : : NULL);
304 : : }
|