pcsc-lite 2.3.0
winscard_msg.c
Go to the documentation of this file.
1/*
2 * MUSCLE SmartCard Development ( https://pcsclite.apdu.fr/ )
3 *
4 * Copyright (C) 2001-2004
5 * David Corcoran <corcoran@musclecard.com>
6 * Copyright (C) 2003-2004
7 * Damien Sauveron <damien.sauveron@labri.fr>
8 * Copyright (C) 2002-2024
9 * Ludovic Rousseau <ludovic.rousseau@free.fr>
10 *
11Redistribution and use in source and binary forms, with or without
12modification, are permitted provided that the following conditions
13are met:
14
151. Redistributions of source code must retain the above copyright
16 notice, this list of conditions and the following disclaimer.
172. Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
203. The name of the author may not be used to endorse or promote products
21 derived from this software without specific prior written permission.
22
23THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
44#include "config.h"
45#include <fcntl.h>
46#include <unistd.h>
47#include <sys/types.h>
48#include <sys/stat.h>
49#include <sys/socket.h>
50#include <sys/time.h>
51#include <sys/un.h>
52#include <sys/ioctl.h>
53#include <errno.h>
54#include <poll.h>
55#include <stdio.h>
56#include <time.h>
57#include <string.h>
58#include <stdlib.h>
59
60#include "misc.h"
61#include "pcscd.h"
62#include "winscard.h"
63#include "debuglog.h"
64#include "winscard_msg.h"
65#include "sys_generic.h"
66#include "utils.h"
67
68#ifdef PCSCD
69
70/* functions used by pcscd only */
71
72#else
73
74/* functions used by libpcsclite only */
75
76#ifndef SOCK_CLOEXEC
77#define SOCK_CLOEXEC 0
78#endif
79
80#define member_size(type, member) sizeof(((type *)0)->member)
81
82static char SocketName[member_size(struct sockaddr_un, sun_path)];
83static pthread_once_t SocketName_init_control = PTHREAD_ONCE_INIT;
84static void SocketName_init(void)
85{
86 /* socket name not yet initialized */
87 const char *socketNameEnv;
88
89 socketNameEnv = SYS_GetEnv("PCSCLITE_CSOCK_NAME");
90 if (socketNameEnv)
91 strncpy(SocketName, socketNameEnv, sizeof SocketName);
92 else
93 strncpy(SocketName, PCSCLITE_CSOCK_NAME, sizeof SocketName);
94
95 /* Ensure a NUL byte */
96 SocketName[sizeof SocketName -1] = '\0';
97}
98
99char *getSocketName(void)
100{
101 pthread_once(&SocketName_init_control, SocketName_init);
102 return SocketName;
103}
104
119INTERNAL int ClientSetupSession(uint32_t *pdwClientID)
120{
121 struct sockaddr_un svc_addr;
122 int ret;
123 char *socketName;
124
125 ret = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
126 if (ret < 0)
127 {
128 Log2(PCSC_LOG_CRITICAL, "Error: create on client socket: %s",
129 strerror(errno));
130 return -1;
131 }
132 *pdwClientID = ret;
133
134 socketName = getSocketName();
135 svc_addr.sun_family = AF_UNIX;
136 strncpy(svc_addr.sun_path, socketName, sizeof(svc_addr.sun_path));
137
138 if (connect(*pdwClientID, (struct sockaddr *) &svc_addr,
139 sizeof(svc_addr.sun_family) + strlen(svc_addr.sun_path) + 1) < 0)
140 {
141 Log3(PCSC_LOG_CRITICAL, "Error: connect to client socket %s: %s",
142 socketName, strerror(errno));
143 (void)close(*pdwClientID);
144 return -1;
145 }
146
147 ret = fcntl(*pdwClientID, F_GETFL, 0);
148 if (ret < 0)
149 {
150 Log3(PCSC_LOG_CRITICAL, "Error: cannot retrieve socket %s flags: %s",
151 socketName, strerror(errno));
152 (void)close(*pdwClientID);
153 return -1;
154 }
155
156 if (fcntl(*pdwClientID, F_SETFL, ret | O_NONBLOCK) < 0)
157 {
158 Log3(PCSC_LOG_CRITICAL, "Error: cannot set socket %s nonblocking: %s",
159 socketName, strerror(errno));
160 (void)close(*pdwClientID);
161 return -1;
162 }
163
164 return 0;
165}
166
173INTERNAL void ClientCloseSession(uint32_t dwClientID)
174{
175 close(dwClientID);
176}
177
195INTERNAL LONG MessageReceiveTimeout(uint32_t command, void *buffer_void,
196 uint64_t buffer_size, int32_t filedes, long timeOut)
197{
198 char *buffer = buffer_void;
199
200 /* default is success */
201 LONG retval = SCARD_S_SUCCESS;
202
203 /* record the time when we started */
204 struct timeval start;
205
206 /* how many bytes we must read */
207 size_t remaining = buffer_size;
208
209 gettimeofday(&start, NULL);
210
211 /* repeat until we get the whole message */
212 while (remaining > 0)
213 {
214 struct pollfd read_fd;
215 struct timeval now;
216 int pollret;
217 long delta;
218
219 gettimeofday(&now, NULL);
220 delta = time_sub(&now, &start) / 1000;
221
222 if (delta > timeOut)
223 {
224 /* we already timed out */
225 retval = SCARD_E_TIMEOUT;
226 break;
227 }
228
229 /* remaining time to wait */
230 delta = timeOut - delta;
231
232 read_fd.fd = filedes;
233 read_fd.events = POLLIN;
234 read_fd.revents = 0;
235
236 pollret = poll(&read_fd, 1, delta);
237
238 /* try to read only when socket is readable */
239 if (pollret > 0)
240 {
241 ssize_t bytes_read;
242
243 if (!(read_fd.revents & POLLIN))
244 {
245 /* very strange situation. it should be an assert really */
246 retval = SCARD_F_COMM_ERROR;
247 break;
248 }
249 bytes_read = read(filedes, buffer, remaining);
250
251 if (bytes_read > 0)
252 {
253 /* we got something */
254 buffer += bytes_read;
255 remaining -= bytes_read;
256 } else if (bytes_read == 0)
257 {
258 /* peer closed the socket */
259 retval = SCARD_F_COMM_ERROR;
260 break;
261 } else
262 {
263 /* we ignore the signals and empty socket situations, all
264 * other errors are fatal */
265 if (errno != EINTR && errno != EAGAIN)
266 {
267 retval = SCARD_F_COMM_ERROR;
268 break;
269 }
270 }
271 } else if (pollret == 0)
272 {
273 /* is the daemon still there? */
274 retval = SCardCheckDaemonAvailability();
275 if (retval != SCARD_S_SUCCESS)
276 {
277 /* timeout */
278 break;
279 }
280
281 /* you need to set the env variable PCSCLITE_DEBUG=0 since
282 * this is logged on the client side and not on the pcscd
283 * side*/
284#ifdef NO_LOG
285 (void)command;
286#endif
287 Log2(PCSC_LOG_INFO, "Command 0x%X not yet finished", command);
288 } else
289 {
290 /* we ignore signals, all other errors are fatal */
291 if (errno != EINTR)
292 {
293 Log2(PCSC_LOG_ERROR, "select returns with failure: %s",
294 strerror(errno));
295 retval = SCARD_F_COMM_ERROR;
296 break;
297 }
298 }
299 }
300
301 return retval;
302}
303
318INTERNAL LONG MessageSendWithHeader(uint32_t command, uint32_t dwClientID,
319 uint64_t size, void *data_void)
320{
321 struct rxHeader header;
322 LONG ret;
323
324 /* header */
325 header.command = command;
326 header.size = size;
327 ret = MessageSend(&header, sizeof(header), dwClientID);
328
329 /* command */
330 if (size > 0)
331 ret = MessageSend(data_void, size, dwClientID);
332
333 return ret;
334}
335
336#endif
337
338/* functions used by pcscd and libpcsclite */
339
355INTERNAL LONG MessageSend(void *buffer_void, uint64_t buffer_size,
356 int32_t filedes)
357{
358 char *buffer = buffer_void;
359
360 /* default is success */
361 LONG retval = SCARD_S_SUCCESS;
362
363 /* how many bytes remains to be written */
364 size_t remaining = buffer_size;
365
366 /* repeat until all data is written */
367 while (remaining > 0)
368 {
369 struct pollfd write_fd;
370 int pollret;
371
372 write_fd.fd = filedes;
373 write_fd.events = POLLOUT;
374 write_fd.revents = 0;
375
376 pollret = poll(&write_fd, 1, -1);
377
378 /* try to write only when the file descriptor is writable */
379 if (pollret > 0)
380 {
381 ssize_t written;
382
383 if (!(write_fd.revents & POLLOUT))
384 {
385 /* very strange situation. it should be an assert really */
386 retval = SCARD_F_COMM_ERROR;
387 break;
388 }
389 /* since we are a user library we can't play with signals
390 * The signals may already be used by the application */
391#ifdef MSG_NOSIGNAL
392 /* Get EPIPE return code instead of SIGPIPE signal
393 * Works on Linux */
394 written = send(filedes, buffer, remaining, MSG_NOSIGNAL);
395#else
396 /* we may get a SIGPIPE signal if the other side has closed */
397 written = write(filedes, buffer, remaining);
398#endif
399
400 if (written > 0)
401 {
402 /* we wrote something */
403 buffer += written;
404 remaining -= written;
405 } else if (written == 0)
406 {
407 /* peer closed the socket */
408 retval = SCARD_F_COMM_ERROR;
409 break;
410 } else
411 {
412 /* we ignore the signals and socket full situations, all
413 * other errors are fatal */
414 if (errno != EINTR && errno != EAGAIN)
415 {
416 retval = SCARD_E_NO_SERVICE;
417 break;
418 }
419 }
420 } else if (pollret == 0)
421 {
422 /* timeout */
423 retval = SCARD_E_TIMEOUT;
424 break;
425 } else
426 {
427 /* ignore signals */
428 if (errno != EINTR)
429 {
430 Log2(PCSC_LOG_ERROR, "select returns with failure: %s",
431 strerror(errno));
432 retval = SCARD_F_COMM_ERROR;
433 break;
434 }
435 }
436 }
437
438 return retval;
439}
440
455INTERNAL LONG MessageReceive(void *buffer_void, uint64_t buffer_size,
456 int32_t filedes)
457{
458 char *buffer = buffer_void;
459
460 /* default is success */
461 LONG retval = SCARD_S_SUCCESS;
462
463 /* how many bytes we must read */
464 size_t remaining = buffer_size;
465
466 /* repeat until we get the whole message */
467 while (remaining > 0)
468 {
469 struct pollfd read_fd;
470 int pollret;
471
472 read_fd.fd = filedes;
473 read_fd.events = POLLIN;
474 read_fd.revents = 0;
475
476 pollret = poll(&read_fd, 1 , -1);
477
478 /* try to read only when socket is readable */
479 if (pollret > 0)
480 {
481 ssize_t bytes_read;
482
483 if (!(read_fd.revents & POLLIN))
484 {
485 /* very strange situation. it should be an assert really */
486 retval = SCARD_F_COMM_ERROR;
487 break;
488 }
489 bytes_read = read(filedes, buffer, remaining);
490
491 if (bytes_read > 0)
492 {
493 /* we got something */
494 buffer += bytes_read;
495 remaining -= bytes_read;
496 } else if (bytes_read == 0)
497 {
498 /* peer closed the socket */
499 retval = SCARD_F_COMM_ERROR;
500 break;
501 } else
502 {
503 /* we ignore the signals and empty socket situations, all
504 * other errors are fatal */
505 if (errno != EINTR && errno != EAGAIN)
506 {
507 /* connection reset by pcscd? */
508 if (ECONNRESET == errno)
510 else
511 retval = SCARD_F_COMM_ERROR;
512 break;
513 }
514 }
515 }
516 else
517 {
518 /* we ignore signals, all other errors are fatal */
519 if (errno != EINTR)
520 {
521 Log2(PCSC_LOG_ERROR, "select returns with failure: %s",
522 strerror(errno));
523 retval = SCARD_F_COMM_ERROR;
524 break;
525 }
526 }
527 }
528
529 return retval;
530}
531
This handles debugging.
#define SCARD_W_SECURITY_VIOLATION
Access was denied because of a security violation.
Definition pcsclite.h:222
#define SCARD_S_SUCCESS
No error was encountered.
Definition pcsclite.h:107
#define SCARD_F_COMM_ERROR
An internal communications error has been detected.
Definition pcsclite.h:145
#define SCARD_E_TIMEOUT
The user-specified timeout value has expired.
Definition pcsclite.h:127
#define SCARD_E_NO_SERVICE
The Smart card resource manager is not running.
Definition pcsclite.h:165
header structure for client/server message data exchange.
uint32_t size
size of the message excluding this header
uint32_t command
one of the pcsc_msg_commands
This handles abstract system level calls.
const char * SYS_GetEnv(const char *name)
(More) secure version of getenv(3)
Definition sys_unix.c:168
long int time_sub(struct timeval *a, struct timeval *b)
return the difference (as long int) in µs between 2 struct timeval r = a - b
Definition utils.c:138
This handles smart card reader communications.
INTERNAL int ClientSetupSession(uint32_t *pdwClientID)
Prepares a communication channel for the client to talk to the server.
INTERNAL LONG MessageReceiveTimeout(uint32_t command, void *buffer_void, uint64_t buffer_size, int32_t filedes, long timeOut)
Called by the Client to get the response from the server or vice-versa.
INTERNAL LONG MessageSendWithHeader(uint32_t command, uint32_t dwClientID, uint64_t size, void *data_void)
Wrapper for the MessageSend() function.
INTERNAL LONG MessageSend(void *buffer_void, uint64_t buffer_size, int32_t filedes)
Sends a menssage from client to server or vice-versa.
INTERNAL void ClientCloseSession(uint32_t dwClientID)
Closes the socket used by the client to communicate with the server.
INTERNAL LONG MessageReceive(void *buffer_void, uint64_t buffer_size, int32_t filedes)
Called by the Client to get the response from the server or vice-versa.
This defines some structures and #defines to be used over the transport layer.