1 /*
2     Copyright (C) 2009-2015 Red Hat, Inc.
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but 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 library; if not, see <http://www.gnu.org/licenses/>.
16 */
17 #include <config.h>
18 
19 #include "smartcard-channel-client.h"
20 
21 struct SmartCardChannelClientPrivate
22 {
23     SPICE_CXX_GLIB_ALLOCATOR
24 
25     red::weak_ptr<RedCharDeviceSmartcard> smartcard;
26 
27     /* read_from_client/write_to_device buffer.
28      * The beginning of the buffer should always be VSCMsgHeader*/
29     RedCharDeviceWriteBuffer *write_buf = nullptr;
30     /* was the client msg received into a RedCharDeviceWriteBuffer
31      * or was it explicitly malloced */
32     bool msg_in_write_buf = false;
33 };
34 
35 struct RedErrorItem: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_ERROR> {
36     VSCMsgHeader vheader;
37     VSCMsgError  error;
38 };
39 
SmartCardChannelClient(RedChannel * channel,RedClient * client,RedStream * stream,RedChannelCapabilities * caps)40 SmartCardChannelClient::SmartCardChannelClient(RedChannel *channel,
41                                                RedClient *client,
42                                                RedStream *stream,
43                                                RedChannelCapabilities *caps):
44     RedChannelClient(channel, client, stream, caps)
45 {
46 }
47 
~SmartCardChannelClient()48 SmartCardChannelClient::~SmartCardChannelClient()
49 {
50 }
51 
smartcard_channel_client_create(RedChannel * channel,RedClient * client,RedStream * stream,RedChannelCapabilities * caps)52 SmartCardChannelClient* smartcard_channel_client_create(RedChannel *channel,
53                                                         RedClient *client, RedStream *stream,
54                                                         RedChannelCapabilities *caps)
55 {
56     auto rcc =
57         red::make_shared<SmartCardChannelClient>(channel, client, stream, caps);
58     if (!rcc->init()) {
59         return nullptr;
60     }
61     return rcc.get();
62 }
63 
64 uint8_t *
alloc_recv_buf(uint16_t type,uint32_t size)65 SmartCardChannelClient::alloc_recv_buf(uint16_t type, uint32_t size)
66 {
67     /* TODO: only one reader is actually supported. When we fix the code to support
68      * multiple readers, we will probably associate different devices to
69      * different channels */
70     if (auto smartcard = priv->smartcard.lock()) {
71         spice_assert(smartcard_get_n_readers() == 1);
72         spice_assert(smartcard_char_device_get_client(smartcard.get()));
73         spice_assert(!priv->write_buf);
74         priv->write_buf =
75             smartcard->write_buffer_get_client((RedCharDeviceClientOpaque *)this,
76                                                size);
77 
78         if (!priv->write_buf) {
79             spice_error("failed to allocate write buffer");
80             return NULL;
81         }
82         priv->msg_in_write_buf = TRUE;
83         return priv->write_buf->buf;
84     }
85     priv->msg_in_write_buf = FALSE;
86     return (uint8_t *) g_malloc(size);
87 }
88 
89 void
release_recv_buf(uint16_t type,uint32_t size,uint8_t * msg)90 SmartCardChannelClient::release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg)
91 {
92     /* todo: only one reader is actually supported. When we fix the code to support
93      * multiple readers, we will porbably associate different devices to
94      * differenc channels */
95 
96     if (!priv->msg_in_write_buf) {
97         spice_assert(!priv->write_buf);
98         g_free(msg);
99     } else {
100         if (priv->write_buf) { /* msg hasn't been pushed to the guest */
101             spice_assert(priv->write_buf->buf == msg);
102             auto smartcard = priv->smartcard.lock();
103             RedCharDevice::write_buffer_release(smartcard.get(), &priv->write_buf);
104         }
105     }
106 }
107 
on_disconnect()108 void SmartCardChannelClient::on_disconnect()
109 {
110     if (auto device = priv->smartcard.lock()) {
111         smartcard_char_device_detach_client(device.get(), this);
112         smartcard_char_device_notify_reader_remove(device.get());
113     }
114 }
115 
smartcard_channel_client_send_data(RedChannelClient * rcc,SpiceMarshaller * m,RedPipeItem * item,VSCMsgHeader * vheader)116 void smartcard_channel_client_send_data(RedChannelClient *rcc,
117                                         SpiceMarshaller *m,
118                                         RedPipeItem *item,
119                                         VSCMsgHeader *vheader)
120 {
121     spice_assert(rcc);
122     spice_assert(vheader);
123     rcc->init_send_data(SPICE_MSG_SMARTCARD_DATA);
124     /* NOTE: 'vheader' is assumed to be owned by 'item' so we keep the pipe
125      * item valid until the message is actually sent. */
126     item->add_to_marshaller(m, (uint8_t*)vheader, sizeof(VSCMsgHeader) + vheader->length);
127 }
128 
smartcard_channel_client_send_error(RedChannelClient * rcc,SpiceMarshaller * m,RedPipeItem * item)129 void smartcard_channel_client_send_error(RedChannelClient *rcc, SpiceMarshaller *m, RedPipeItem *item)
130 {
131     RedErrorItem* error_item = static_cast<RedErrorItem*>(item);
132 
133     smartcard_channel_client_send_data(rcc, m, item, &error_item->vheader);
134 }
135 
smartcard_channel_client_push_error(RedChannelClient * rcc,uint32_t reader_id,VSCErrorCode error)136 static void smartcard_channel_client_push_error(RedChannelClient *rcc,
137                                                 uint32_t reader_id,
138                                                 VSCErrorCode error)
139 {
140     auto error_item = red::make_shared<RedErrorItem>();
141 
142     error_item->vheader.reader_id = reader_id;
143     error_item->vheader.type = VSC_Error;
144     error_item->vheader.length = sizeof(error_item->error);
145     error_item->error.code = error;
146     rcc->pipe_add_push(error_item);
147 }
148 
smartcard_channel_client_add_reader(SmartCardChannelClient * scc)149 static void smartcard_channel_client_add_reader(SmartCardChannelClient *scc)
150 {
151     auto smartcard = scc->priv->smartcard.lock();
152     if (!smartcard) { /* we already tried to attach a reader to the client
153                                           when it connected */
154         SpiceCharDeviceInstance *char_device = smartcard_readers_get_unattached();
155 
156         if (!char_device) {
157             smartcard_channel_client_push_error(scc,
158                                                 VSCARD_UNDEFINED_READER_ID,
159                                                 VSC_CANNOT_ADD_MORE_READERS);
160             return;
161         }
162         smartcard_char_device_attach_client(char_device, scc);
163         smartcard = scc->priv->smartcard.lock();
164     }
165     smartcard_char_device_notify_reader_add(smartcard.get());
166     // The device sends a VSC_Error message, we will let it through, no
167     // need to send our own. We already set the correct reader_id, from
168     // our RedCharDeviceSmartcard.
169 }
170 
171 XXX_CAST(RedCharDevice, RedCharDeviceSmartcard, RED_CHAR_DEVICE_SMARTCARD);
172 
smartcard_channel_client_remove_reader(SmartCardChannelClient * scc,uint32_t reader_id)173 static void smartcard_channel_client_remove_reader(SmartCardChannelClient *scc,
174                                                    uint32_t reader_id)
175 {
176     SpiceCharDeviceInstance *char_device = smartcard_readers_get(reader_id);
177     RedCharDeviceSmartcard *dev;
178 
179     if (char_device == NULL) {
180         smartcard_channel_client_push_error(scc,
181                                             reader_id, VSC_GENERAL_ERROR);
182         return;
183     }
184 
185     dev = RED_CHAR_DEVICE_SMARTCARD(char_device->st);
186     spice_assert(scc->priv->smartcard.lock().get() == dev);
187     if (!smartcard_char_device_notify_reader_remove(dev)) {
188         smartcard_channel_client_push_error(scc,
189                                             reader_id, VSC_GENERAL_ERROR);
190         return;
191     }
192 }
193 
smartcard_channel_client_write_to_reader(SmartCardChannelClient * scc)194 static void smartcard_channel_client_write_to_reader(SmartCardChannelClient *scc)
195 {
196     g_return_if_fail(scc);
197 
198     smartcard_channel_write_to_reader(scc->priv->write_buf);
199     scc->priv->write_buf = NULL;
200 }
201 
202 
handle_message(uint16_t type,uint32_t size,void * message)203 bool SmartCardChannelClient::handle_message(uint16_t type, uint32_t size, void *message)
204 {
205     VSCMsgHeader* vheader = (VSCMsgHeader*) message;
206 
207     if (type != SPICE_MSGC_SMARTCARD_DATA) {
208         /* Handles seamless migration protocol. Also handles ack's */
209         return RedChannelClient::handle_message(type, size, message);
210     }
211 
212     switch (vheader->type) {
213         case VSC_ReaderAdd:
214             smartcard_channel_client_add_reader(this);
215             return TRUE;
216             break;
217         case VSC_ReaderRemove:
218             smartcard_channel_client_remove_reader(this, vheader->reader_id);
219             return TRUE;
220             break;
221         case VSC_Init:
222             // ignore - we should never get this anyway
223             return TRUE;
224             break;
225         case VSC_Error:
226         case VSC_ATR:
227         case VSC_CardRemove:
228         case VSC_APDU:
229             break; // passed on to device
230         default:
231             red_channel_warning(get_channel(),
232                                 "ERROR: unexpected message on smartcard channel");
233             return TRUE;
234     }
235 
236     /* todo: fix */
237     if (vheader->reader_id >= smartcard_get_n_readers()) {
238         red_channel_warning(get_channel(),
239                             "ERROR: received message for non existing reader: %d, %d, %d",
240                             vheader->reader_id, vheader->type, vheader->length);
241         return FALSE;
242     }
243     spice_assert(priv->write_buf->buf_size >= size);
244     memcpy(priv->write_buf->buf, message, size);
245     smartcard_channel_client_write_to_reader(this);
246 
247     return TRUE;
248 }
249 
handle_migrate_data(uint32_t size,void * message)250 bool SmartCardChannelClient::handle_migrate_data(uint32_t size, void *message)
251 {
252     SmartCardChannelClient *scc = this;
253     SpiceMigrateDataHeader *header;
254     SpiceMigrateDataSmartcard *mig_data;
255 
256     header = (SpiceMigrateDataHeader *)message;
257     mig_data = (SpiceMigrateDataSmartcard *)(header + 1);
258     if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataSmartcard)) {
259         spice_error("bad message size");
260         return FALSE;
261     }
262     if (!migration_protocol_validate_header(header,
263                                             SPICE_MIGRATE_DATA_SMARTCARD_MAGIC,
264                                             SPICE_MIGRATE_DATA_SMARTCARD_VERSION)) {
265         spice_error("bad header");
266         return FALSE;
267     }
268 
269     if (!mig_data->base.connected) { /* client wasn't attached to a smartcard */
270         return TRUE;
271     }
272 
273     auto smartcard = scc->priv->smartcard.lock();
274     if (!smartcard) {
275         SpiceCharDeviceInstance *char_device = smartcard_readers_get_unattached();
276 
277         if (!char_device) {
278             spice_warning("no unattached device available");
279             return TRUE;
280         } else {
281             smartcard_char_device_attach_client(char_device, scc);
282         }
283         smartcard = scc->priv->smartcard.lock();
284     }
285     spice_debug("reader added %d partial read_size %u", mig_data->reader_added, mig_data->read_size);
286 
287     if (smartcard) {
288         return smartcard_char_device_handle_migrate_data(smartcard.get(), mig_data);
289     }
290     return TRUE;
291 }
292 
handle_migrate_flush_mark()293 void SmartCardChannelClient::handle_migrate_flush_mark()
294 {
295     pipe_add_type(RED_PIPE_ITEM_TYPE_SMARTCARD_MIGRATE_DATA);
296 }
297 
smartcard_channel_client_set_char_device(SmartCardChannelClient * scc,RedCharDeviceSmartcard * device)298 void smartcard_channel_client_set_char_device(SmartCardChannelClient *scc,
299                                               RedCharDeviceSmartcard *device)
300 {
301     scc->priv->smartcard.reset(device);
302 }
303 
304 red::shared_ptr<RedCharDeviceSmartcard>
smartcard_channel_client_get_char_device(SmartCardChannelClient * scc)305 smartcard_channel_client_get_char_device(SmartCardChannelClient *scc)
306 {
307     return scc->priv->smartcard.lock();
308 }
309