1 /*
2  * Copyright (C) 2010 Stefan Walter
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as
6  * published by the Free Software Foundation; either version 2.1 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include "gcr-key-mechanisms.h"
21 
22 #include <glib/gi18n-lib.h>
23 
24 static gboolean
check_have_attributes(GckAttributes * attrs,const gulong * types,gsize n_types)25 check_have_attributes (GckAttributes *attrs,
26                        const gulong *types,
27                        gsize n_types)
28 {
29 	gsize i;
30 
31 	for (i = 0; i < n_types; i++) {
32 		if (!gck_attributes_find (attrs, types[i]))
33 			return FALSE;
34 	}
35 
36 	return TRUE;
37 }
38 
39 static gulong
find_first_usable_mechanism(GckObject * key,GckAttributes * attrs,const gulong * mechanisms,gsize n_mechanisms,gulong action_attr_type)40 find_first_usable_mechanism (GckObject *key,
41                              GckAttributes *attrs,
42                              const gulong *mechanisms,
43                              gsize n_mechanisms,
44                              gulong action_attr_type)
45 {
46 	GckSession *session;
47 	GckSlot *slot;
48 	GArray *mechs;
49 	gboolean can;
50 	gsize i;
51 
52 	if (gck_attributes_find_boolean (attrs, action_attr_type, &can) && !can) {
53 		g_debug ("key not capable of needed action");
54 		return GCK_INVALID;
55 	}
56 
57 	session = gck_object_get_session (key);
58 	slot = gck_session_get_slot (session);
59 	mechs = gck_slot_get_mechanisms (slot);
60 	g_object_unref (slot);
61 	g_object_unref (session);
62 
63 	if (!mechs) {
64 		g_debug ("couldn't get slot mechanisms");
65 		return GCK_INVALID;
66 	}
67 
68 	for (i = 0; i < n_mechanisms; i++) {
69 		if (gck_mechanisms_check (mechs, mechanisms[i], GCK_INVALID))
70 			break;
71 	}
72 
73 	gck_mechanisms_free (mechs);
74 
75 	if (i < n_mechanisms)
76 		return mechanisms[i];
77 	return GCK_INVALID;
78 }
79 
80 gulong
_gcr_key_mechanisms_check(GckObject * key,const gulong * mechanisms,gsize n_mechanisms,gulong action_attr_type,GCancellable * cancellable,GError ** error)81 _gcr_key_mechanisms_check (GckObject *key,
82                            const gulong *mechanisms,
83                            gsize n_mechanisms,
84                            gulong action_attr_type,
85                            GCancellable *cancellable,
86                            GError **error)
87 {
88 	gulong attr_types[] = { action_attr_type };
89 	GckAttributes *attrs = NULL;
90 	gulong result;
91 
92 	g_return_val_if_fail (GCK_IS_OBJECT (key), GCK_INVALID);
93 	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), GCK_INVALID);
94 	g_return_val_if_fail (error == NULL || *error == NULL, GCK_INVALID);
95 
96 	if (GCK_IS_OBJECT_CACHE (key)) {
97 		attrs = gck_object_cache_get_attributes (GCK_OBJECT_CACHE (key));
98 		if (!check_have_attributes (attrs, attr_types, G_N_ELEMENTS (attr_types))) {
99 			gck_attributes_unref (attrs);
100 			attrs = NULL;
101 		}
102 	}
103 
104 	if (attrs == NULL) {
105 		attrs = gck_object_get_full (key, attr_types, G_N_ELEMENTS (attr_types),
106 		                             cancellable, error);
107 	}
108 
109 	if (!attrs)
110 		return GCK_INVALID;
111 
112 	result = find_first_usable_mechanism (key, attrs, mechanisms, n_mechanisms, action_attr_type);
113 	gck_attributes_unref (attrs);
114 	return result;
115 }
116 
117 typedef struct {
118 	gulong *mechanisms;
119 	gsize n_mechanisms;
120 	gulong action_attr_type;
121 } CheckClosure;
122 
123 static void
check_closure_free(gpointer data)124 check_closure_free (gpointer data)
125 {
126 	CheckClosure *closure = data;
127 	g_free (closure->mechanisms);
128 	g_free (closure);
129 }
130 
131 static void
on_check_get_attributes(GObject * source,GAsyncResult * result,gpointer user_data)132 on_check_get_attributes (GObject *source,
133                          GAsyncResult *result,
134                          gpointer user_data)
135 {
136 	GTask *task = G_TASK (user_data);
137 	GckAttributes *attrs;
138 	GError *error = NULL;
139 
140 	attrs = gck_object_cache_lookup_finish (GCK_OBJECT (source), result, &error);
141 	if (error != NULL)
142 		g_task_return_error (task, g_steal_pointer (&error));
143 	else
144 		g_task_return_pointer (task, attrs, gck_attributes_unref);
145 
146 	g_clear_object (&task);
147 }
148 
149 void
_gcr_key_mechanisms_check_async(GckObject * key,const gulong * mechanisms,gsize n_mechanisms,gulong action_attr_type,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)150 _gcr_key_mechanisms_check_async (GckObject *key,
151                                  const gulong *mechanisms,
152                                  gsize n_mechanisms,
153                                  gulong action_attr_type,
154                                  GCancellable *cancellable,
155                                  GAsyncReadyCallback callback,
156                                  gpointer user_data)
157 {
158 	gulong attr_types[] = { action_attr_type };
159 	CheckClosure *closure;
160 	GTask *task;
161 
162 	g_return_if_fail (GCK_IS_OBJECT (key));
163 	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
164 
165 	task = g_task_new (key, cancellable, callback, user_data);
166 	g_task_set_source_tag (task, _gcr_key_mechanisms_check_async);
167 	closure = g_new0 (CheckClosure, 1);
168 	closure->mechanisms = g_memdup (mechanisms, n_mechanisms * sizeof (gulong));
169 	closure->n_mechanisms = n_mechanisms;
170 	closure->action_attr_type = action_attr_type;
171 	g_task_set_task_data (task, closure, check_closure_free);
172 
173 	gck_object_cache_lookup_async (key, attr_types, G_N_ELEMENTS (attr_types),
174 	                               cancellable, on_check_get_attributes,
175 	                               g_steal_pointer (&task));
176 
177 	g_clear_object (&task);
178 }
179 
180 gulong
_gcr_key_mechanisms_check_finish(GckObject * key,GAsyncResult * result,GError ** error)181 _gcr_key_mechanisms_check_finish (GckObject *key,
182                                   GAsyncResult *result,
183                                   GError **error)
184 {
185 	CheckClosure *closure;
186 	GckAttributes *attrs;
187 	gulong ret = GCK_INVALID;
188 
189 	g_return_val_if_fail (GCK_IS_OBJECT (key), GCK_INVALID);
190 	g_return_val_if_fail (error == NULL || *error == NULL, GCK_INVALID);
191 
192 	g_return_val_if_fail (g_task_is_valid (result, key), GCK_INVALID);
193 	g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) ==
194 	                      _gcr_key_mechanisms_check_async, GCK_INVALID);
195 
196 	closure = g_task_get_task_data (G_TASK (result));
197 
198 	attrs = g_task_propagate_pointer (G_TASK (result), error);
199 	if (!attrs)
200 		return GCK_INVALID;
201 
202 	ret = find_first_usable_mechanism (GCK_OBJECT (key), attrs,
203 	                                   closure->mechanisms, closure->n_mechanisms,
204 	                                   closure->action_attr_type);
205 
206 	gck_attributes_unref (attrs);
207 	return ret;
208 }
209