Branch data Line data Source code
1 : : /*
2 : : * gnome-keyring
3 : : *
4 : : * Copyright (C) 2014 Stef Walter
5 : : * Copyright (C) 2018 Red Hat, Inc.
6 : : *
7 : : * This program is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU Lesser General Public License as
9 : : * published by the Free Software Foundation; either version 2.1 of
10 : : * the License, or (at your option) any later version.
11 : : *
12 : : * This program is distributed in the hope that it will be useful, but
13 : : * WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General Public
18 : : * License along with this program; if not, see
19 : : * <http://www.gnu.org/licenses/>.
20 : : *
21 : : * Author: Stef Walter <stef@thewalter.net>, Daiki Ueno
22 : : */
23 : :
24 : : #include "config.h"
25 : :
26 : : #include "gcr-ssh-agent-preload.h"
27 : :
28 : : #include "gcr-ssh-agent-util.h"
29 : : #include "egg/egg-file-tracker.h"
30 : : #include <string.h>
31 : :
32 : : enum {
33 : : PROP_0,
34 : : PROP_PATH
35 : : };
36 : :
37 : : struct _GcrSshAgentPreload
38 : : {
39 : : GObject object;
40 : :
41 : : gchar *path;
42 : : GHashTable *keys_by_public_filename;
43 : : GHashTable *keys_by_public_key;
44 : : EggFileTracker *file_tracker;
45 : : GMutex lock;
46 : : };
47 : :
48 [ + + + - : 110 : G_DEFINE_TYPE (GcrSshAgentPreload, gcr_ssh_agent_preload, G_TYPE_OBJECT);
+ + ]
49 : :
50 : : void
51 : 31 : gcr_ssh_agent_key_info_free (gpointer boxed)
52 : : {
53 : 31 : GcrSshAgentKeyInfo *info = boxed;
54 [ - + ]: 31 : if (!info)
55 : 0 : return;
56 : 31 : g_bytes_unref (info->public_key);
57 : 31 : g_free (info->comment);
58 : 31 : g_free (info->filename);
59 : 31 : g_free (info);
60 : : }
61 : :
62 : : gpointer
63 : 17 : gcr_ssh_agent_key_info_copy (gpointer boxed)
64 : : {
65 : 17 : GcrSshAgentKeyInfo *info = boxed;
66 : 17 : GcrSshAgentKeyInfo *copy = g_new0 (GcrSshAgentKeyInfo, 1);
67 : 17 : copy->public_key = g_bytes_ref (info->public_key);
68 : 17 : copy->comment = g_strdup (info->comment);
69 : 17 : copy->filename = g_strdup (info->filename);
70 : 17 : return copy;
71 : : }
72 : :
73 : : static void file_load_inlock (EggFileTracker *tracker,
74 : : const gchar *path,
75 : : gpointer user_data);
76 : : static void file_remove_inlock (EggFileTracker *tracker,
77 : : const gchar *path,
78 : : gpointer user_data);
79 : :
80 : : static void
81 : 19 : gcr_ssh_agent_preload_init (GcrSshAgentPreload *self)
82 : : {
83 : 19 : self->keys_by_public_filename = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
84 : 19 : self->keys_by_public_key = g_hash_table_new_full (g_bytes_hash, g_bytes_equal, NULL, gcr_ssh_agent_key_info_free);
85 : 19 : }
86 : :
87 : : static void
88 : 19 : gcr_ssh_agent_preload_constructed (GObject *object)
89 : : {
90 : 19 : GcrSshAgentPreload *self = GCR_SSH_AGENT_PRELOAD (object);
91 : :
92 : 19 : self->file_tracker = egg_file_tracker_new (self->path, "*.pub", NULL);
93 : 19 : g_signal_connect (self->file_tracker, "file-added", G_CALLBACK (file_load_inlock), self);
94 : 19 : g_signal_connect (self->file_tracker, "file-removed", G_CALLBACK (file_remove_inlock), self);
95 : 19 : g_signal_connect (self->file_tracker, "file-changed", G_CALLBACK (file_load_inlock), self);
96 : :
97 : 19 : G_OBJECT_CLASS (gcr_ssh_agent_preload_parent_class)->constructed (object);
98 : 19 : }
99 : :
100 : : static void
101 : 19 : gcr_ssh_agent_preload_set_property (GObject *object,
102 : : guint prop_id,
103 : : const GValue *value,
104 : : GParamSpec *pspec)
105 : : {
106 : 19 : GcrSshAgentPreload *self = GCR_SSH_AGENT_PRELOAD (object);
107 : :
108 [ + - ]: 19 : switch (prop_id) {
109 : 19 : case PROP_PATH:
110 : 19 : self->path = g_value_dup_string (value);
111 : 19 : break;
112 : 0 : default:
113 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
114 : 0 : break;
115 : : }
116 : 19 : }
117 : :
118 : : static void
119 : 19 : gcr_ssh_agent_preload_finalize (GObject *object)
120 : : {
121 : 19 : GcrSshAgentPreload *self = GCR_SSH_AGENT_PRELOAD (object);
122 : :
123 : 19 : g_free (self->path);
124 [ + - ]: 19 : g_clear_pointer (&self->keys_by_public_key, g_hash_table_unref);
125 [ + - ]: 19 : g_clear_pointer (&self->keys_by_public_filename, g_hash_table_unref);
126 [ + - ]: 19 : g_clear_object (&self->file_tracker);
127 : :
128 : 19 : g_mutex_clear (&self->lock);
129 : :
130 : 19 : G_OBJECT_CLASS (gcr_ssh_agent_preload_parent_class)->finalize (object);
131 : 19 : }
132 : :
133 : : static void
134 : 2 : gcr_ssh_agent_preload_class_init (GcrSshAgentPreloadClass *klass)
135 : : {
136 : 2 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
137 : 2 : gobject_class->constructed = gcr_ssh_agent_preload_constructed;
138 : 2 : gobject_class->set_property = gcr_ssh_agent_preload_set_property;
139 : 2 : gobject_class->finalize = gcr_ssh_agent_preload_finalize;
140 : 2 : g_object_class_install_property (gobject_class, PROP_PATH,
141 : : g_param_spec_string ("path", "Path", "Path",
142 : : "",
143 : : G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
144 : 2 : }
145 : :
146 : : static gchar *
147 : 14 : private_path_for_public (const gchar *public_path)
148 : : {
149 [ + - ]: 14 : if (g_str_has_suffix (public_path, ".pub"))
150 : 14 : return g_strndup (public_path, strlen (public_path) - 4);
151 : :
152 : 0 : return NULL;
153 : : }
154 : :
155 : : static GBytes *
156 : 28 : file_get_contents (const gchar *path,
157 : : gboolean must_be_present)
158 : : {
159 : 28 : GError *error = NULL;
160 : : gchar *contents;
161 : : gsize length;
162 : :
163 [ - + ]: 28 : if (!g_file_get_contents (path, &contents, &length, &error)) {
164 [ # # # # ]: 0 : if (must_be_present || error->code != G_FILE_ERROR_NOENT)
165 : 0 : g_message ("couldn't read file: %s: %s", path, error->message);
166 : 0 : g_error_free (error);
167 : 0 : return NULL;
168 : : }
169 : :
170 : 28 : return g_bytes_new_take (contents, length);
171 : : }
172 : :
173 : : static void
174 : 15 : file_remove_inlock (EggFileTracker *tracker,
175 : : const gchar *path,
176 : : gpointer user_data)
177 : : {
178 : 15 : GcrSshAgentPreload *self = GCR_SSH_AGENT_PRELOAD (user_data);
179 : : GcrSshAgentKeyInfo *info;
180 : :
181 : 15 : info = g_hash_table_lookup (self->keys_by_public_filename, path);
182 [ + + ]: 15 : if (info) {
183 : 2 : g_hash_table_remove (self->keys_by_public_filename, path);
184 : 2 : g_hash_table_remove (self->keys_by_public_key, info->public_key);
185 : : }
186 : 15 : }
187 : :
188 : : static void
189 : 14 : file_load_inlock (EggFileTracker *tracker,
190 : : const gchar *path,
191 : : gpointer user_data)
192 : : {
193 : 14 : GcrSshAgentPreload *self = GCR_SSH_AGENT_PRELOAD (user_data);
194 : : gchar *private_path;
195 : : GBytes *private_bytes;
196 : : GBytes *public_bytes;
197 : : GBytes *public_key;
198 : : GcrSshAgentKeyInfo *info;
199 : : gchar *comment;
200 : :
201 : 14 : file_remove_inlock (tracker, path, user_data);
202 : :
203 : 14 : private_path = private_path_for_public (path);
204 : :
205 : 14 : private_bytes = file_get_contents (private_path, FALSE);
206 [ - + ]: 14 : if (!private_bytes) {
207 : 0 : g_debug ("no private key present for public key: %s", path);
208 : 0 : g_free (private_path);
209 : 0 : return;
210 : : }
211 : :
212 : 14 : public_bytes = file_get_contents (path, TRUE);
213 [ + - ]: 14 : if (public_bytes) {
214 : 14 : public_key = _gcr_ssh_agent_parse_public_key (public_bytes, &comment);
215 [ + - ]: 14 : if (public_key) {
216 : 14 : info = g_new0 (GcrSshAgentKeyInfo, 1);
217 : 14 : info->filename = private_path;
218 : 14 : private_path = NULL;
219 : 14 : info->public_key = public_key;
220 : 14 : info->comment = comment;
221 : 14 : g_hash_table_replace (self->keys_by_public_filename, g_strdup (path), info);
222 : 14 : g_hash_table_replace (self->keys_by_public_key, info->public_key, info);
223 : : } else {
224 : 0 : g_message ("failed to parse ssh public key: %s", path);
225 : : }
226 : :
227 : 14 : g_bytes_unref (public_bytes);
228 : : }
229 : :
230 : 14 : g_bytes_unref (private_bytes);
231 : 14 : g_free (private_path);
232 : : }
233 : :
234 : : GcrSshAgentPreload *
235 : 19 : gcr_ssh_agent_preload_new (const gchar *path)
236 : : {
237 [ - + ]: 19 : g_return_val_if_fail (path, NULL);
238 : :
239 : 19 : return g_object_new (GCR_TYPE_SSH_AGENT_PRELOAD, "path", path, NULL);
240 : : }
241 : :
242 : : GList *
243 : 16 : gcr_ssh_agent_preload_get_keys (GcrSshAgentPreload *self)
244 : : {
245 : 16 : GList *keys = NULL;
246 : : GHashTableIter iter;
247 : : GcrSshAgentKeyInfo *info;
248 : :
249 : 16 : g_mutex_lock (&self->lock);
250 : :
251 : 16 : egg_file_tracker_refresh (self->file_tracker, FALSE);
252 : :
253 : 16 : g_hash_table_iter_init (&iter, self->keys_by_public_key);
254 [ + + ]: 32 : while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&info))
255 : 16 : keys = g_list_prepend (keys, gcr_ssh_agent_key_info_copy (info));
256 : :
257 : 16 : g_mutex_unlock (&self->lock);
258 : :
259 : 16 : return keys;
260 : : }
261 : :
262 : : GcrSshAgentKeyInfo *
263 : 2 : gcr_ssh_agent_preload_lookup_by_public_key (GcrSshAgentPreload *self,
264 : : GBytes *public_key)
265 : : {
266 : : GcrSshAgentKeyInfo *info;
267 : :
268 : 2 : g_mutex_lock (&self->lock);
269 : :
270 : 2 : egg_file_tracker_refresh (self->file_tracker, FALSE);
271 : :
272 : 2 : info = g_hash_table_lookup (self->keys_by_public_key, public_key);
273 [ + + ]: 2 : if (info)
274 : 1 : info = gcr_ssh_agent_key_info_copy (info);
275 : :
276 : 2 : g_mutex_unlock (&self->lock);
277 : :
278 : 2 : return info;
279 : : }
|