1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 Copyright (C) 2011 Red Hat, Inc.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "config.h"
19
20 #include "spice-client.h"
21 #include "spice-common.h"
22
23 #include "spice-channel-priv.h"
24 #include "smartcard-manager.h"
25 #include "smartcard-manager-priv.h"
26 #include "spice-session-priv.h"
27
28 /**
29 * SECTION:channel-smartcard
30 * @short_description: smartcard authentication
31 * @title: Smartcard Channel
32 * @section_id:
33 * @see_also: #SpiceSmartcardManager, #SpiceSession
34 * @stability: Stable
35 * @include: spice-client.h
36 *
37 * The Spice protocol defines a set of messages to forward smartcard
38 * information from the Spice client to the VM. This channel handles
39 * these messages. While it's mainly focus on smartcard readers and
40 * smartcards, it's also possible to use it with a software smartcard
41 * (ie a set of 3 certificates from the client machine).
42 * This class doesn't provide useful methods, see #SpiceSession properties
43 * for a way to enable/disable this channel, and #SpiceSmartcardManager
44 * if you want to detect smartcard reader hotplug/unplug, and smartcard
45 * insertion/removal.
46 */
47
48 struct _SpiceSmartcardChannelMessage {
49 #ifdef USE_SMARTCARD
50 VSCMsgType message_type;
51 #endif
52 SpiceMsgOut *message;
53 };
54 typedef struct _SpiceSmartcardChannelMessage SpiceSmartcardChannelMessage;
55
56
57 struct _SpiceSmartcardChannelPrivate {
58 /* track readers that have been added but for which we didn't receive
59 * an ack from the spice server yet. We rely on the fact that the
60 * readers in this list are ordered by the time we sent the request to
61 * the server. When we get an ack from the server for a reader addition,
62 * we can pop the 1st entry to get the reader the ack corresponds to. */
63 GList *pending_reader_additions;
64
65 /* used to removals of readers that were not ack'ed yet by the spice
66 * server */
67 GHashTable *pending_reader_removals;
68
69 /* used to track card insertions on readers that were not ack'ed yet
70 * by the spice server */
71 GHashTable *pending_card_insertions;
72
73 /* next commands to be sent to the spice server. This is needed since
74 * we have to wait for a command answer before sending the next one
75 */
76 GQueue *message_queue;
77
78 /* message that is currently being processed by the spice server (ie last
79 * message that was sent to the server)
80 */
81 SpiceSmartcardChannelMessage *in_flight_message;
82 };
83
84 G_DEFINE_TYPE_WITH_PRIVATE(SpiceSmartcardChannel, spice_smartcard_channel, SPICE_TYPE_CHANNEL)
85
86 enum {
87
88 SPICE_SMARTCARD_LAST_SIGNAL,
89 };
90
91 static void spice_smartcard_channel_up(SpiceChannel *channel);
92 static void handle_smartcard_msg(SpiceChannel *channel, SpiceMsgIn *in);
93 static void smartcard_message_free(SpiceSmartcardChannelMessage *message);
94
95 /* ------------------------------------------------------------------ */
96 #ifdef USE_SMARTCARD
97 static void reader_added_cb(SpiceSmartcardManager *manager, VReader *reader,
98 gpointer user_data);
99 static void reader_removed_cb(SpiceSmartcardManager *manager, VReader *reader,
100 gpointer user_data);
101 static void card_inserted_cb(SpiceSmartcardManager *manager, VReader *reader,
102 gpointer user_data);
103 static void card_removed_cb(SpiceSmartcardManager *manager, VReader *reader,
104 gpointer user_data);
105 #endif
106
spice_smartcard_channel_init(SpiceSmartcardChannel * channel)107 static void spice_smartcard_channel_init(SpiceSmartcardChannel *channel)
108 {
109 SpiceSmartcardChannelPrivate *priv;
110
111 channel->priv = spice_smartcard_channel_get_instance_private(channel);
112 priv = channel->priv;
113 priv->message_queue = g_queue_new();
114
115 #ifdef USE_SMARTCARD
116 priv->pending_card_insertions =
117 g_hash_table_new_full(g_direct_hash, g_direct_equal,
118 (GDestroyNotify)vreader_free, NULL);
119 priv->pending_reader_removals =
120 g_hash_table_new_full(g_direct_hash, g_direct_equal,
121 (GDestroyNotify)vreader_free, NULL);
122 #endif
123 }
124
spice_smartcard_channel_constructed(GObject * object)125 static void spice_smartcard_channel_constructed(GObject *object)
126 {
127 SpiceSession *s = spice_channel_get_session(SPICE_CHANNEL(object));
128
129 g_return_if_fail(s != NULL);
130
131 #ifdef USE_SMARTCARD
132 if (!spice_session_is_for_migration(s)) {
133 SpiceSmartcardChannel *channel = SPICE_SMARTCARD_CHANNEL(object);
134 SpiceSmartcardManager *manager = spice_smartcard_manager_get();
135
136 spice_g_signal_connect_object(G_OBJECT(manager), "reader-added",
137 (GCallback)reader_added_cb, channel, 0);
138 spice_g_signal_connect_object(G_OBJECT(manager), "reader-removed",
139 (GCallback)reader_removed_cb, channel, 0);
140 spice_g_signal_connect_object(G_OBJECT(manager), "card-inserted",
141 (GCallback)card_inserted_cb, channel, 0);
142 spice_g_signal_connect_object(G_OBJECT(manager), "card-removed",
143 (GCallback)card_removed_cb, channel, 0);
144 }
145 #endif
146
147 if (G_OBJECT_CLASS(spice_smartcard_channel_parent_class)->constructed)
148 G_OBJECT_CLASS(spice_smartcard_channel_parent_class)->constructed(object);
149
150 }
151
spice_smartcard_channel_finalize(GObject * obj)152 static void spice_smartcard_channel_finalize(GObject *obj)
153 {
154 SpiceSmartcardChannel *channel = SPICE_SMARTCARD_CHANNEL(obj);
155 SpiceSmartcardChannelPrivate *c = channel->priv;
156
157 g_clear_pointer(&c->pending_card_insertions, g_hash_table_destroy);
158 g_clear_pointer(&c->pending_reader_removals, g_hash_table_destroy);
159 if (c->message_queue != NULL) {
160 g_queue_foreach(c->message_queue, (GFunc)smartcard_message_free, NULL);
161 g_queue_free(c->message_queue);
162 c->message_queue = NULL;
163 }
164 g_clear_pointer(&c->in_flight_message, smartcard_message_free);
165 g_clear_pointer(&c->pending_reader_additions, g_list_free);
166
167 if (G_OBJECT_CLASS(spice_smartcard_channel_parent_class)->finalize)
168 G_OBJECT_CLASS(spice_smartcard_channel_parent_class)->finalize(obj);
169 }
170
spice_smartcard_channel_reset(SpiceChannel * channel,gboolean migrating)171 static void spice_smartcard_channel_reset(SpiceChannel *channel, gboolean migrating)
172 {
173 SpiceSmartcardChannel *smartcard_channel = SPICE_SMARTCARD_CHANNEL(channel);
174 SpiceSmartcardChannelPrivate *c = smartcard_channel->priv;
175
176 g_hash_table_remove_all(c->pending_card_insertions);
177 g_hash_table_remove_all(c->pending_reader_removals);
178
179 if (c->message_queue != NULL) {
180 g_queue_foreach(c->message_queue, (GFunc)smartcard_message_free, NULL);
181 g_queue_clear(c->message_queue);
182 }
183
184 g_clear_pointer(&c->in_flight_message, smartcard_message_free);
185 g_clear_pointer(&c->pending_reader_additions, g_list_free);
186
187 SPICE_CHANNEL_CLASS(spice_smartcard_channel_parent_class)->channel_reset(channel, migrating);
188 }
189
channel_set_handlers(SpiceChannelClass * klass)190 static void channel_set_handlers(SpiceChannelClass *klass)
191 {
192 static const spice_msg_handler handlers[] = {
193 [ SPICE_MSG_SMARTCARD_DATA ] = handle_smartcard_msg,
194 };
195 spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
196 }
197
spice_smartcard_channel_class_init(SpiceSmartcardChannelClass * klass)198 static void spice_smartcard_channel_class_init(SpiceSmartcardChannelClass *klass)
199 {
200 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
201 SpiceChannelClass *channel_class = SPICE_CHANNEL_CLASS(klass);
202
203 gobject_class->finalize = spice_smartcard_channel_finalize;
204 gobject_class->constructed = spice_smartcard_channel_constructed;
205
206 channel_class->channel_up = spice_smartcard_channel_up;
207 channel_class->channel_reset = spice_smartcard_channel_reset;
208
209 channel_set_handlers(SPICE_CHANNEL_CLASS(klass));
210 }
211
212 /* ------------------------------------------------------------------ */
213 /* private api */
214
215 static void
smartcard_message_free(SpiceSmartcardChannelMessage * message)216 smartcard_message_free(SpiceSmartcardChannelMessage *message)
217 {
218 if (message->message)
219 spice_msg_out_unref(message->message);
220 g_free(message);
221 }
222
223 #ifdef USE_SMARTCARD
is_attached_to_server(VReader * reader)224 static gboolean is_attached_to_server(VReader *reader)
225 {
226 return (vreader_get_id(reader) != (vreader_id_t)-1);
227 }
228
229 static gboolean
spice_channel_has_pending_card_insertion(SpiceSmartcardChannel * channel,VReader * reader)230 spice_channel_has_pending_card_insertion(SpiceSmartcardChannel *channel,
231 VReader *reader)
232 {
233 return (g_hash_table_lookup(channel->priv->pending_card_insertions, reader) != NULL);
234 }
235
236 static void
spice_channel_queue_card_insertion(SpiceSmartcardChannel * channel,VReader * reader)237 spice_channel_queue_card_insertion(SpiceSmartcardChannel *channel,
238 VReader *reader)
239 {
240 vreader_reference(reader);
241 g_hash_table_insert(channel->priv->pending_card_insertions,
242 reader, reader);
243 }
244
245 static void
spice_channel_drop_pending_card_insertion(SpiceSmartcardChannel * channel,VReader * reader)246 spice_channel_drop_pending_card_insertion(SpiceSmartcardChannel *channel,
247 VReader *reader)
248 {
249 g_hash_table_remove(channel->priv->pending_card_insertions, reader);
250 }
251
252 static gboolean
spice_channel_has_pending_reader_removal(SpiceSmartcardChannel * channel,VReader * reader)253 spice_channel_has_pending_reader_removal(SpiceSmartcardChannel *channel,
254 VReader *reader)
255 {
256 return (g_hash_table_lookup(channel->priv->pending_reader_removals, reader) != NULL);
257 }
258
259 static void
spice_channel_queue_reader_removal(SpiceSmartcardChannel * channel,VReader * reader)260 spice_channel_queue_reader_removal(SpiceSmartcardChannel *channel,
261 VReader *reader)
262 {
263 vreader_reference(reader);
264 g_hash_table_insert(channel->priv->pending_reader_removals,
265 reader, reader);
266 }
267
268 static void
spice_channel_drop_pending_reader_removal(SpiceSmartcardChannel * channel,VReader * reader)269 spice_channel_drop_pending_reader_removal(SpiceSmartcardChannel *channel,
270 VReader *reader)
271 {
272 g_hash_table_remove(channel->priv->pending_reader_removals, reader);
273 }
274
275 static SpiceSmartcardChannelMessage *
smartcard_message_new(VSCMsgType msg_type,SpiceMsgOut * msg_out)276 smartcard_message_new(VSCMsgType msg_type, SpiceMsgOut *msg_out)
277 {
278 SpiceSmartcardChannelMessage *message;
279
280 message = g_new0(SpiceSmartcardChannelMessage, 1);
281 message->message = msg_out;
282 message->message_type = msg_type;
283
284 return message;
285 }
286
287 /* Indicates that handling of the message that is currently in flight has
288 * been completed. If needed, sends the next queued command to the server. */
289 static void
smartcard_message_complete_in_flight(SpiceSmartcardChannel * channel)290 smartcard_message_complete_in_flight(SpiceSmartcardChannel *channel)
291 {
292 g_return_if_fail(channel->priv->in_flight_message != NULL);
293
294 smartcard_message_free(channel->priv->in_flight_message);
295 channel->priv->in_flight_message = g_queue_pop_head(channel->priv->message_queue);
296 if (channel->priv->in_flight_message != NULL) {
297 spice_msg_out_send(channel->priv->in_flight_message->message);
298 channel->priv->in_flight_message->message = NULL;
299 }
300 }
301
smartcard_message_send(SpiceSmartcardChannel * channel,VSCMsgType msg_type,SpiceMsgOut * msg_out,gboolean queue)302 static void smartcard_message_send(SpiceSmartcardChannel *channel,
303 VSCMsgType msg_type,
304 SpiceMsgOut *msg_out, gboolean queue)
305 {
306 SpiceSmartcardChannelMessage *message;
307
308 if (spice_channel_get_read_only(SPICE_CHANNEL(channel)))
309 return;
310
311 CHANNEL_DEBUG(channel, "send message %u, %s",
312 msg_type, queue ? "queued" : "now");
313 if (!queue) {
314 spice_msg_out_send(msg_out);
315 return;
316 }
317
318 message = smartcard_message_new(msg_type, msg_out);
319 if (channel->priv->in_flight_message == NULL) {
320 g_return_if_fail(g_queue_is_empty(channel->priv->message_queue));
321 channel->priv->in_flight_message = message;
322 spice_msg_out_send(channel->priv->in_flight_message->message);
323 channel->priv->in_flight_message->message = NULL;
324 } else {
325 g_queue_push_tail(channel->priv->message_queue, message);
326 }
327 }
328
329 static void
send_msg_generic_with_data(SpiceSmartcardChannel * channel,VReader * reader,VSCMsgType msg_type,const uint8_t * data,gsize data_len,gboolean serialize_msg)330 send_msg_generic_with_data(SpiceSmartcardChannel *channel, VReader *reader,
331 VSCMsgType msg_type,
332 const uint8_t *data, gsize data_len,
333 gboolean serialize_msg)
334 {
335 SpiceMsgOut *msg_out;
336 VSCMsgHeader header = {
337 .type = msg_type,
338 .length = data_len
339 };
340
341 if(vreader_get_id(reader) == -1)
342 header.reader_id = VSCARD_UNDEFINED_READER_ID;
343 else
344 header.reader_id = vreader_get_id(reader);
345
346 msg_out = spice_msg_out_new(SPICE_CHANNEL(channel),
347 SPICE_MSGC_SMARTCARD_DATA);
348 msg_out->marshallers->msgc_smartcard_header(msg_out->marshaller, &header);
349 if ((data != NULL) && (data_len != 0)) {
350 spice_marshaller_add(msg_out->marshaller, data, data_len);
351 }
352
353 smartcard_message_send(channel, msg_type, msg_out, serialize_msg);
354 }
355
send_msg_generic(SpiceSmartcardChannel * channel,VReader * reader,VSCMsgType msg_type)356 static void send_msg_generic(SpiceSmartcardChannel *channel, VReader *reader,
357 VSCMsgType msg_type)
358 {
359 send_msg_generic_with_data(channel, reader, msg_type, NULL, 0, TRUE);
360 }
361
send_msg_atr(SpiceSmartcardChannel * channel,VReader * reader)362 static void send_msg_atr(SpiceSmartcardChannel *channel, VReader *reader)
363 {
364 #define MAX_ATR_LEN 40 //this should be defined in libcacard
365 uint8_t atr[MAX_ATR_LEN];
366 int atr_len = MAX_ATR_LEN;
367
368 g_return_if_fail(vreader_get_id(reader) != VSCARD_UNDEFINED_READER_ID);
369 vreader_power_on(reader, atr, &atr_len);
370 send_msg_generic_with_data(channel, reader, VSC_ATR, atr, atr_len, TRUE);
371 }
372
reader_added_cb(SpiceSmartcardManager * manager,VReader * reader,gpointer user_data)373 static void reader_added_cb(SpiceSmartcardManager *manager, VReader *reader,
374 gpointer user_data)
375 {
376 SpiceSmartcardChannel *channel = SPICE_SMARTCARD_CHANNEL(user_data);
377 const char *reader_name = vreader_get_name(reader);
378
379 if (vreader_get_id(reader) != -1 ||
380 g_list_find(channel->priv->pending_reader_additions, reader))
381 return;
382
383 channel->priv->pending_reader_additions =
384 g_list_append(channel->priv->pending_reader_additions, reader);
385
386 send_msg_generic_with_data(channel, reader, VSC_ReaderAdd,
387 (uint8_t*)reader_name, strlen(reader_name), TRUE);
388 }
389
reader_removed_cb(SpiceSmartcardManager * manager,VReader * reader,gpointer user_data)390 static void reader_removed_cb(SpiceSmartcardManager *manager, VReader *reader,
391 gpointer user_data)
392 {
393 SpiceSmartcardChannel *channel = SPICE_SMARTCARD_CHANNEL(user_data);
394
395 if (is_attached_to_server(reader)) {
396 send_msg_generic(channel, reader, VSC_ReaderRemove);
397 } else {
398 spice_channel_queue_reader_removal(channel, reader);
399 }
400 }
401
402 /* ------------------------------------------------------------------ */
403 /* callbacks */
card_inserted_cb(SpiceSmartcardManager * manager,VReader * reader,gpointer user_data)404 static void card_inserted_cb(SpiceSmartcardManager *manager, VReader *reader,
405 gpointer user_data)
406 {
407 SpiceSmartcardChannel *channel = SPICE_SMARTCARD_CHANNEL(user_data);
408
409 if (is_attached_to_server(reader)) {
410 send_msg_atr(channel, reader);
411 } else {
412 spice_channel_queue_card_insertion(channel, reader);
413 }
414 }
415
card_removed_cb(SpiceSmartcardManager * manager,VReader * reader,gpointer user_data)416 static void card_removed_cb(SpiceSmartcardManager *manager, VReader *reader,
417 gpointer user_data)
418 {
419 SpiceSmartcardChannel *channel = SPICE_SMARTCARD_CHANNEL(user_data);
420
421 if (is_attached_to_server(reader)) {
422 send_msg_generic(channel, reader, VSC_CardRemove);
423 } else {
424 /* this does nothing when reader has no card insertion pending */
425 spice_channel_drop_pending_card_insertion(channel, reader);
426 }
427 }
428 #endif /* USE_SMARTCARD */
429
spice_smartcard_channel_up_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)430 static void spice_smartcard_channel_up_cb(GObject *source_object,
431 GAsyncResult *res,
432 gpointer user_data)
433 {
434 SpiceChannel *channel = SPICE_CHANNEL(user_data);
435 #ifdef USE_SMARTCARD
436 SpiceSmartcardManager *manager = spice_smartcard_manager_get();
437 GList *l, *list = NULL;
438 #endif
439 GError *error = NULL;
440
441 g_return_if_fail(channel != NULL);
442 g_return_if_fail(SPICE_IS_SESSION(source_object));
443
444 spice_smartcard_manager_init_finish(SPICE_SESSION(source_object),
445 res, &error);
446 if (error) {
447 g_warning("%s", error->message);
448 goto end;
449 }
450
451 #ifdef USE_SMARTCARD
452 list = spice_smartcard_manager_get_readers(manager);
453 for (l = list; l != NULL; l = l->next) {
454 VReader *reader = l->data;
455 gboolean has_card = vreader_card_is_present(reader) == VREADER_OK;
456
457 reader_added_cb(manager, reader, channel);
458 if (has_card)
459 card_inserted_cb(manager, reader, channel);
460
461 g_boxed_free(SPICE_TYPE_SMARTCARD_READER, reader);
462 }
463 #endif
464
465 end:
466 #ifdef USE_SMARTCARD
467 g_list_free(list);
468 #endif
469 g_clear_error(&error);
470 }
471
spice_smartcard_channel_up(SpiceChannel * channel)472 static void spice_smartcard_channel_up(SpiceChannel *channel)
473 {
474 if (spice_session_is_for_migration(spice_channel_get_session(channel)))
475 return;
476
477 spice_smartcard_manager_init_async(spice_channel_get_session(channel),
478 g_cancellable_new(),
479 spice_smartcard_channel_up_cb,
480 channel);
481 }
482
handle_smartcard_msg(SpiceChannel * channel,SpiceMsgIn * in)483 static void handle_smartcard_msg(SpiceChannel *channel, SpiceMsgIn *in)
484 {
485 #ifdef USE_SMARTCARD
486 SpiceSmartcardChannel *smartcard_channel = SPICE_SMARTCARD_CHANNEL(channel);
487 SpiceSmartcardChannelPrivate *priv = smartcard_channel->priv;
488 SpiceMsgSmartcard *msg = spice_msg_in_parsed(in);
489 VReader *reader;
490
491 CHANNEL_DEBUG(channel, "handle msg %u", msg->type);
492 switch (msg->type) {
493 case VSC_Error:
494 g_return_if_fail(priv->in_flight_message != NULL);
495 CHANNEL_DEBUG(channel, "in flight %u", priv->in_flight_message->message_type);
496 switch (priv->in_flight_message->message_type) {
497 case VSC_ReaderAdd:
498 g_return_if_fail(priv->pending_reader_additions != NULL);
499 reader = priv->pending_reader_additions->data;
500 g_return_if_fail(reader != NULL);
501 g_return_if_fail(vreader_get_id(reader) == -1);
502 priv->pending_reader_additions =
503 g_list_delete_link(priv->pending_reader_additions,
504 priv->pending_reader_additions);
505 vreader_set_id(reader, msg->reader_id);
506
507 if (spice_channel_has_pending_card_insertion(smartcard_channel, reader)) {
508 send_msg_atr(smartcard_channel, reader);
509 spice_channel_drop_pending_card_insertion(smartcard_channel, reader);
510 }
511
512 if (spice_channel_has_pending_reader_removal(smartcard_channel, reader)) {
513 send_msg_generic(smartcard_channel, reader, VSC_CardRemove);
514 spice_channel_drop_pending_reader_removal(smartcard_channel, reader);
515 }
516 break;
517 case VSC_APDU:
518 case VSC_ATR:
519 case VSC_CardRemove:
520 case VSC_Error:
521 case VSC_ReaderRemove:
522 break;
523 default:
524 g_warning("Unexpected message: %u", priv->in_flight_message->message_type);
525 break;
526 }
527 smartcard_message_complete_in_flight(smartcard_channel);
528
529 break;
530
531 case VSC_APDU:
532 case VSC_Init: {
533 const unsigned int APDU_BUFFER_SIZE = 270;
534 VReaderStatus reader_status;
535 uint8_t data_out[APDU_BUFFER_SIZE + sizeof(uint32_t)];
536 int data_out_len = sizeof(data_out);
537
538 g_return_if_fail(msg->reader_id != VSCARD_UNDEFINED_READER_ID);
539 reader = vreader_get_reader_by_id(msg->reader_id);
540 g_return_if_fail(reader != NULL); //FIXME: add log message
541
542 reader_status = vreader_xfr_bytes(reader,
543 msg->data, msg->length,
544 data_out, &data_out_len);
545 if (reader_status == VREADER_OK) {
546 send_msg_generic_with_data(smartcard_channel,
547 reader, VSC_APDU,
548 data_out, data_out_len, FALSE);
549 } else {
550 uint32_t error_code;
551 error_code = GUINT32_TO_LE(reader_status);
552 send_msg_generic_with_data(smartcard_channel,
553 reader, VSC_Error,
554 (uint8_t*)&error_code,
555 sizeof (error_code), FALSE);
556 }
557 break;
558 }
559 default:
560 g_return_if_reached();
561 }
562 #endif
563 }
564