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