pcsc-lite  1.8.11
winscard_msg.c
Go to the documentation of this file.
1 /*
2  * MUSCLE SmartCard Development ( http://pcsclite.alioth.debian.org/pcsclite.html )
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-2010
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 Changes to this license can be made only by the copyright author with
24 explicit written consent.
25 
26 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  *
37  * $Id: winscard_msg.c 6851 2014-02-14 15:43:32Z rousseau $
38  */
39 
49 #include "config.h"
50 #include <fcntl.h>
51 #include <unistd.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/socket.h>
55 #include <sys/time.h>
56 #include <sys/un.h>
57 #include <sys/ioctl.h>
58 #include <errno.h>
59 #include <stdio.h>
60 #include <time.h>
61 #include <string.h>
62 #include <stdlib.h>
63 #ifdef HAVE_SYS_FILIO_H
64 #include <sys/filio.h>
65 #endif
66 
67 #include "misc.h"
68 #include "pcscd.h"
69 #include "winscard.h"
70 #include "debuglog.h"
71 #include "winscard_msg.h"
72 #include "sys_generic.h"
73 #include "utils.h"
74 #include "strlcpycat.h"
75 
76 #ifdef PCSCD
77 
78 /* functions used by pcscd only */
79 
80 #else
81 
82 /* functions used by libpcsclite only */
83 
84 char *getSocketName(void)
85 {
86  static char socketName[sizeof(struct sockaddr_un)];
87 
88  if ('\0' == socketName[0])
89  {
90  /* socket name not yet initialized */
91  char *socketNameEnv;
92 
93  socketNameEnv = getenv("PCSCLITE_CSOCK_NAME");
94  if (socketNameEnv)
95  strlcpy(socketName, socketNameEnv, sizeof(socketName));
96  else
97  strlcpy(socketName, PCSCLITE_CSOCK_NAME, sizeof(socketName));
98  }
99 
100  return socketName;
101 }
102 
116 INTERNAL int ClientSetupSession(uint32_t *pdwClientID)
117 {
118  struct sockaddr_un svc_addr;
119  int ret;
120  char *socketName;
121 
122  ret = socket(PF_UNIX, SOCK_STREAM, 0);
123  if (ret < 0)
124  {
125  Log2(PCSC_LOG_CRITICAL, "Error: create on client socket: %s",
126  strerror(errno));
127  return -1;
128  }
129  *pdwClientID = ret;
130 
131  socketName = getSocketName();
132  svc_addr.sun_family = AF_UNIX;
133  strncpy(svc_addr.sun_path, socketName, sizeof(svc_addr.sun_path));
134 
135  if (connect(*pdwClientID, (struct sockaddr *) &svc_addr,
136  sizeof(svc_addr.sun_family) + strlen(svc_addr.sun_path) + 1) < 0)
137  {
138  Log3(PCSC_LOG_CRITICAL, "Error: connect to client socket %s: %s",
139  socketName, strerror(errno));
140  (void)close(*pdwClientID);
141  return -1;
142  }
143 
144  ret = fcntl(*pdwClientID, F_GETFL, 0);
145  if (ret < 0)
146  {
147  Log3(PCSC_LOG_CRITICAL, "Error: cannot retrieve socket %s flags: %s",
148  socketName, strerror(errno));
149  (void)close(*pdwClientID);
150  return -1;
151  }
152 
153  if (fcntl(*pdwClientID, F_SETFL, ret | O_NONBLOCK) < 0)
154  {
155  Log3(PCSC_LOG_CRITICAL, "Error: cannot set socket %s nonblocking: %s",
156  socketName, strerror(errno));
157  (void)close(*pdwClientID);
158  return -1;
159  }
160 
161  return 0;
162 }
163 
171 INTERNAL int ClientCloseSession(uint32_t dwClientID)
172 {
173  return close(dwClientID);
174 }
175 
192 INTERNAL LONG MessageReceiveTimeout(uint32_t command, void *buffer_void,
193  uint64_t buffer_size, int32_t filedes, long timeOut)
194 {
195  char *buffer = buffer_void;
196 
197  /* default is success */
198  LONG retval = SCARD_S_SUCCESS;
199 
200  /* record the time when we started */
201  struct timeval start;
202 
203  /* how many bytes we must read */
204  size_t remaining = buffer_size;
205 
206  gettimeofday(&start, NULL);
207 
208  /* repeat until we get the whole message */
209  while (remaining > 0)
210  {
211  fd_set read_fd;
212  struct timeval timeout, now;
213  int selret;
214  long delta;
215 
216  gettimeofday(&now, NULL);
217  delta = time_sub(&now, &start);
218 
219  if (delta > timeOut*1000)
220  {
221  /* we already timed out */
222  retval = SCARD_E_TIMEOUT;
223  break;
224  }
225 
226  /* remaining time to wait */
227  delta = timeOut*1000 - delta;
228 
229  FD_ZERO(&read_fd);
230  FD_SET(filedes, &read_fd);
231 
232  timeout.tv_sec = delta/1000000;
233  timeout.tv_usec = delta - timeout.tv_sec*1000000;
234 
235  selret = select(filedes + 1, &read_fd, NULL, NULL, &timeout);
236 
237  /* try to read only when socket is readable */
238  if (selret > 0)
239  {
240  int readed;
241 
242  if (!FD_ISSET(filedes, &read_fd))
243  {
244  /* very strange situation. it should be an assert really */
245  retval = SCARD_F_COMM_ERROR;
246  break;
247  }
248  readed = read(filedes, buffer, remaining);
249 
250  if (readed > 0)
251  {
252  /* we got something */
253  buffer += readed;
254  remaining -= readed;
255  } else if (readed == 0)
256  {
257  /* peer closed the socket */
258  retval = SCARD_F_COMM_ERROR;
259  break;
260  } else
261  {
262  /* we ignore the signals and empty socket situations, all
263  * other errors are fatal */
264  if (errno != EINTR && errno != EAGAIN)
265  {
266  retval = SCARD_F_COMM_ERROR;
267  break;
268  }
269  }
270  } else if (selret == 0)
271  {
272  /* is the daemon still there? */
273  retval = SCardCheckDaemonAvailability();
274  if (retval != SCARD_S_SUCCESS)
275  {
276  /* timeout */
277  break;
278  }
279 
280  /* you need to set the env variable PCSCLITE_DEBUG=0 since
281  * this is logged on the client side and not on the pcscd
282  * side*/
283  Log2(PCSC_LOG_INFO, "Command 0x%X not yet finished", command);
284  } else
285  {
286  /* we ignore signals, all other errors are fatal */
287  if (errno != EINTR)
288  {
289  Log2(PCSC_LOG_ERROR, "select returns with failure: %s",
290  strerror(errno));
291  retval = SCARD_F_COMM_ERROR;
292  break;
293  }
294  }
295  }
296 
297  return retval;
298 }
299 
314 INTERNAL LONG MessageSendWithHeader(uint32_t command, uint32_t dwClientID,
315  uint64_t size, void *data_void)
316 {
317  struct rxHeader header;
318  LONG ret;
319 
320  /* header */
321  header.command = command;
322  header.size = size;
323  ret = MessageSend(&header, sizeof(header), dwClientID);
324 
325  /* command */
326  if (size > 0)
327  ret = MessageSend(data_void, size, dwClientID);
328 
329  return ret;
330 }
331 
332 #endif
333 
334 /* functions used by pcscd and libpcsclite */
335 
350 INTERNAL LONG MessageSend(void *buffer_void, uint64_t buffer_size,
351  int32_t filedes)
352 {
353  char *buffer = buffer_void;
354 
355  /* default is success */
356  LONG retval = SCARD_S_SUCCESS;
357 
358  /* how many bytes remains to be written */
359  size_t remaining = buffer_size;
360 
361  /* repeat until all data is written */
362  while (remaining > 0)
363  {
364  fd_set write_fd;
365  int selret;
366 
367  FD_ZERO(&write_fd);
368  FD_SET(filedes, &write_fd);
369 
370  selret = select(filedes + 1, NULL, &write_fd, NULL, NULL);
371 
372  /* try to write only when the file descriptor is writable */
373  if (selret > 0)
374  {
375  int written;
376 
377  if (!FD_ISSET(filedes, &write_fd))
378  {
379  /* very strange situation. it should be an assert really */
380  retval = SCARD_F_COMM_ERROR;
381  break;
382  }
383  /* since we are a user library we can't play with signals
384  * The signals may already be used by the application */
385 #ifdef MSG_NOSIGNAL
386  /* Get EPIPE return code instead of SIGPIPE signal
387  * Works on Linux */
388  written = send(filedes, buffer, remaining, MSG_NOSIGNAL);
389 #else
390  /* we may get a SIGPIPE signal if the other side has closed */
391  written = write(filedes, buffer, remaining);
392 #endif
393 
394  if (written > 0)
395  {
396  /* we wrote something */
397  buffer += written;
398  remaining -= written;
399  } else if (written == 0)
400  {
401  /* peer closed the socket */
402  retval = SCARD_F_COMM_ERROR;
403  break;
404  } else
405  {
406  /* we ignore the signals and socket full situations, all
407  * other errors are fatal */
408  if (errno != EINTR && errno != EAGAIN)
409  {
410  retval = SCARD_E_NO_SERVICE;
411  break;
412  }
413  }
414  } else if (selret == 0)
415  {
416  /* timeout */
417  retval = SCARD_E_TIMEOUT;
418  break;
419  } else
420  {
421  /* ignore signals */
422  if (errno != EINTR)
423  {
424  Log2(PCSC_LOG_ERROR, "select returns with failure: %s",
425  strerror(errno));
426  retval = SCARD_F_COMM_ERROR;
427  break;
428  }
429  }
430  }
431 
432  return retval;
433 }
434 
448 INTERNAL LONG MessageReceive(void *buffer_void, uint64_t buffer_size,
449  int32_t filedes)
450 {
451  char *buffer = buffer_void;
452 
453  /* default is success */
454  LONG retval = SCARD_S_SUCCESS;
455 
456  /* how many bytes we must read */
457  size_t remaining = buffer_size;
458 
459  /* repeat until we get the whole message */
460  while (remaining > 0)
461  {
462  fd_set read_fd;
463  int selret;
464 
465  FD_ZERO(&read_fd);
466  FD_SET(filedes, &read_fd);
467 
468  selret = select(filedes + 1, &read_fd, NULL, NULL, NULL);
469 
470  /* try to read only when socket is readable */
471  if (selret > 0)
472  {
473  int readed;
474 
475  if (!FD_ISSET(filedes, &read_fd))
476  {
477  /* very strange situation. it should be an assert really */
478  retval = SCARD_F_COMM_ERROR;
479  break;
480  }
481  readed = read(filedes, buffer, remaining);
482 
483  if (readed > 0)
484  {
485  /* we got something */
486  buffer += readed;
487  remaining -= readed;
488  } else if (readed == 0)
489  {
490  /* peer closed the socket */
491  retval = SCARD_F_COMM_ERROR;
492  break;
493  } else
494  {
495  /* we ignore the signals and empty socket situations, all
496  * other errors are fatal */
497  if (errno != EINTR && errno != EAGAIN)
498  {
499  retval = SCARD_F_COMM_ERROR;
500  break;
501  }
502  }
503  }
504  else
505  {
506  /* we ignore signals, all other errors are fatal */
507  if (errno != EINTR)
508  {
509  Log2(PCSC_LOG_ERROR, "select returns with failure: %s",
510  strerror(errno));
511  retval = SCARD_F_COMM_ERROR;
512  break;
513  }
514  }
515  }
516 
517  return retval;
518 }
519 
INTERNAL int ClientCloseSession(uint32_t dwClientID)
Closes the socket used by the client to communicate with the server.
Definition: winscard_msg.c:171
INTERNAL LONG MessageSendWithHeader(uint32_t command, uint32_t dwClientID, uint64_t size, void *data_void)
Wrapper for the MessageSend() function.
Definition: winscard_msg.c:314
#define SCARD_F_COMM_ERROR
An internal communications error has been detected.
Definition: pcsclite.h:125
uint32_t command
one of the pcsc_msg_commands
Definition: winscard_msg.h:72
This handles abstract system level calls.
#define SCARD_E_NO_SERVICE
The Smart card resource manager is not running.
Definition: pcsclite.h:135
header structure for client/server message data exchange.
Definition: winscard_msg.h:69
INTERNAL LONG MessageReceive(void *buffer_void, uint64_t buffer_size, int32_t filedes)
Called by the Client to get the reponse from the server or vice-versa.
Definition: winscard_msg.c:448
prototypes of strlcpy()/strlcat() imported from OpenBSD
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:140
uint32_t size
size of the message excluding this header
Definition: winscard_msg.h:71
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:116
This keeps a list of defines for pcsc-lite.
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:350
#define SCARD_S_SUCCESS
error codes from http://msdn.microsoft.com/en-us/library/aa924526.aspx
Definition: pcsclite.h:106
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 reponse from the server or vice-versa.
Definition: winscard_msg.c:192
This handles debugging.
#define SCARD_E_TIMEOUT
The user-specified timeout value has expired.
Definition: pcsclite.h:116