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