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