1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* gck-call.c - the GObject PKCS#11 wrapper library
3
4 Copyright (C) 2008, Stefan Walter
5 Copyright (C) 2020, Marco Trevisan
6
7 The Gnome Keyring Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 The Gnome Keyring Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public
18 License along with the Gnome Library; see the file COPYING.LIB. If not,
19 see <http://www.gnu.org/licenses/>.
20
21 Author: Stef Walter <nielsen@memberwebs.com>
22 */
23
24 #include "config.h"
25
26 #include "gck-private.h"
27
28 #include <string.h>
29
30 struct _GckCall {
31 GObject parent;
32 GTask *task;
33 GckModule *module;
34
35 /* For making the call */
36 GckPerformFunc perform;
37 GckCompleteFunc complete;
38 GckArguments *args;
39 GDestroyNotify destroy;
40 };
41
G_DEFINE_TYPE(GckCall,_gck_call,G_TYPE_OBJECT)42 G_DEFINE_TYPE (GckCall, _gck_call, G_TYPE_OBJECT)
43
44 /* ----------------------------------------------------------------------------
45 * HELPER FUNCTIONS
46 */
47
48 static CK_RV
49 perform_call (GckPerformFunc func, GCancellable *cancellable, GckArguments *args)
50 {
51 CK_RV rv;
52
53 /* Double check a few things */
54 g_assert (func);
55 g_assert (args);
56
57 if (cancellable) {
58 if (g_cancellable_is_cancelled (cancellable)) {
59 return CKR_FUNCTION_CANCELED;
60 }
61
62 /* Push for the notify callback */
63 g_object_ref (cancellable);
64 g_cancellable_push_current (cancellable);
65 }
66
67 rv = (func) (args);
68
69 if (cancellable) {
70 g_cancellable_pop_current (cancellable);
71 g_object_unref (cancellable);
72 }
73
74 return rv;
75 }
76
77 static gboolean
complete_call(GckCompleteFunc func,GckArguments * args,CK_RV result)78 complete_call (GckCompleteFunc func, GckArguments *args, CK_RV result)
79 {
80 /* Double check a few things */
81 g_assert (args);
82
83 /* If no complete function, then just ignore */
84 if (!func)
85 return TRUE;
86
87 return (func) (args, result);
88 }
89
90 static CK_RV
perform_call_chain(GckPerformFunc perform,GckCompleteFunc complete,GCancellable * cancellable,GckArguments * args)91 perform_call_chain (GckPerformFunc perform, GckCompleteFunc complete,
92 GCancellable *cancellable, GckArguments *args)
93 {
94 CK_RV rv;
95
96 do {
97 rv = perform_call (perform, cancellable, args);
98 if (rv == CKR_FUNCTION_CANCELED)
99 break;
100 } while (!complete_call (complete, args, rv));
101
102 return rv;
103 }
104
105
106 static void
_gck_task_return(GTask * task,CK_RV rv)107 _gck_task_return (GTask *task, CK_RV rv)
108 {
109 if (rv == CKR_OK) {
110 g_task_return_boolean (task, TRUE);
111 } else if (rv == CKR_FUNCTION_CANCELED) {
112 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED,
113 "Gck function call cancelled");
114 } else {
115 g_task_return_new_error (task, GCK_ERROR, rv, "%s",
116 gck_message_from_rv (rv));
117 }
118 }
119
120 static void
_gck_call_thread_func(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)121 _gck_call_thread_func (GTask *task,
122 gpointer source_object,
123 gpointer task_data,
124 GCancellable *cancellable)
125 {
126 GckCall *call = task_data;
127 CK_RV rv;
128
129 /* Double check a few things */
130 g_assert (GCK_IS_CALL (call));
131
132 rv = perform_call_chain (call->perform, call->complete, cancellable,
133 call->args);
134
135 _gck_task_return (task, rv);
136 }
137
138 /* ----------------------------------------------------------------------------
139 * OBJECT
140 */
141
142 static void
_gck_call_init(GckCall * call)143 _gck_call_init (GckCall *call)
144 {
145 }
146
147 static void
_gck_call_finalize(GObject * obj)148 _gck_call_finalize (GObject *obj)
149 {
150 GckCall *call = GCK_CALL (obj);
151
152 if (call->module)
153 g_object_unref (call->module);
154 call->module = NULL;
155
156 g_clear_object (&call->task);
157
158 if (call->destroy)
159 (call->destroy) (call->args);
160 call->destroy = NULL;
161 call->args = NULL;
162
163 G_OBJECT_CLASS (_gck_call_parent_class)->finalize (obj);
164 }
165
166 static void
_gck_call_class_init(GckCallClass * klass)167 _gck_call_class_init (GckCallClass *klass)
168 {
169 GObjectClass *gobject_class = (GObjectClass*)klass;
170
171 gobject_class->finalize = _gck_call_finalize;
172 }
173
174 /* ----------------------------------------------------------------------------
175 * PUBLIC
176 */
177
178 void
_gck_call_uninitialize(void)179 _gck_call_uninitialize (void)
180 {
181
182 }
183
184 gboolean
_gck_call_sync(gpointer object,gpointer perform,gpointer complete,gpointer data,GCancellable * cancellable,GError ** err)185 _gck_call_sync (gpointer object, gpointer perform, gpointer complete,
186 gpointer data, GCancellable *cancellable, GError **err)
187 {
188 GckArguments *args = (GckArguments*)data;
189 GckModule *module = NULL;
190 CK_RV rv;
191
192 g_assert (!object || G_IS_OBJECT (object));
193 g_assert (perform);
194 g_assert (args);
195
196 if (object) {
197 g_object_get (object, "module", &module, "handle", &args->handle, NULL);
198 g_assert (GCK_IS_MODULE (module));
199
200 /* We now hold a reference to module until below */
201 args->pkcs11 = gck_module_get_functions (module);
202 g_assert (args->pkcs11);
203 }
204
205 rv = perform_call_chain (perform, complete, cancellable, args);
206
207 if (module)
208 g_object_unref (module);
209
210 if (rv == CKR_OK)
211 return TRUE;
212
213 g_set_error (err, GCK_ERROR, rv, "%s", gck_message_from_rv (rv));
214 return FALSE;
215 }
216
217 GckCall*
_gck_call_async_prep(gpointer object,gpointer perform,gpointer complete,gsize args_size,gpointer destroy)218 _gck_call_async_prep (gpointer object, gpointer perform, gpointer complete,
219 gsize args_size, gpointer destroy)
220 {
221 GckArguments *args;
222 GckCall *call;
223
224 g_assert (!object || G_IS_OBJECT (object));
225 g_assert (perform);
226
227 if (!destroy)
228 destroy = g_free;
229
230 if (args_size == 0)
231 args_size = sizeof (GckArguments);
232 g_assert (args_size >= sizeof (GckArguments));
233
234 args = g_malloc0 (args_size);
235 call = g_object_new (GCK_TYPE_CALL, NULL);
236 call->destroy = (GDestroyNotify)destroy;
237 call->perform = (GckPerformFunc)perform;
238 call->complete = (GckCompleteFunc)complete;
239
240 /* Hook the two together */
241 call->args = args;
242
243 /* Setup call object if available */
244 if (object != NULL)
245 _gck_call_async_object (call, object);
246
247 return call;
248 }
249
250 void
_gck_call_async_object(GckCall * call,gpointer object)251 _gck_call_async_object (GckCall *call, gpointer object)
252 {
253 g_assert (GCK_IS_CALL (call));
254 g_assert (call->args);
255
256 if (call->module)
257 g_object_unref (call->module);
258 call->module = NULL;
259
260 g_object_get (object, "module", &call->module, "handle", &call->args->handle, NULL);
261 g_assert (GCK_IS_MODULE (call->module));
262 call->args->pkcs11 = gck_module_get_functions (call->module);
263
264 /* We now hold a reference on module until finalize */
265 }
266
267 GckCall*
_gck_call_async_ready(GckCall * call,gpointer cb_object,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)268 _gck_call_async_ready (GckCall *call, gpointer cb_object,
269 GCancellable *cancellable, GAsyncReadyCallback callback,
270 gpointer user_data)
271 {
272 GTask* task;
273
274 g_assert (GCK_IS_CALL (call));
275 g_assert (call->args && "GckCall not prepared");
276 g_assert (!cb_object || G_IS_OBJECT (cb_object));
277
278 g_object_ref (call);
279
280 task = g_task_new (cb_object, cancellable, callback, user_data);
281 g_task_set_task_data (task, call, g_object_unref);
282 g_set_object (&call->task, task);
283
284 g_object_unref (task);
285 g_object_unref (call);
286
287 return call;
288 }
289
290 void
_gck_call_async_go(GckCall * call)291 _gck_call_async_go (GckCall *call)
292 {
293 g_assert (GCK_IS_CALL (call));
294 g_assert (G_IS_TASK (call->task));
295
296 g_task_run_in_thread (call->task, _gck_call_thread_func);
297 g_clear_object (&call->task);
298 }
299
300 void
_gck_call_async_ready_go(GckCall * call,gpointer cb_object,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)301 _gck_call_async_ready_go (GckCall *call, gpointer cb_object,
302 GCancellable *cancellable,
303 GAsyncReadyCallback callback, gpointer user_data)
304 {
305 _gck_call_async_ready (call, cb_object, cancellable, callback, user_data);
306 _gck_call_async_go (call);
307 }
308
309 gboolean
_gck_call_basic_finish(GAsyncResult * result,GError ** err)310 _gck_call_basic_finish (GAsyncResult *result, GError **err)
311 {
312 g_return_val_if_fail (G_IS_TASK (result), FALSE);
313
314 return g_task_propagate_boolean (G_TASK (result), err);
315 }
316
317 void
_gck_call_async_short(GckCall * call,CK_RV rv)318 _gck_call_async_short (GckCall *call, CK_RV rv)
319 {
320 g_assert (GCK_IS_CALL (call));
321
322 /* Already complete, so just push it for processing in main loop */
323 _gck_task_return (call->task, rv);
324 g_clear_object (&call->task);
325
326 g_main_context_wakeup (NULL);
327 }
328
329 gpointer
_gck_call_get_arguments(GckCall * call)330 _gck_call_get_arguments (GckCall *call)
331 {
332 g_assert (GCK_IS_CALL (call));
333 return call->args;
334 }
335