1 /*
2     Generate events on card status change
3     Copyrigt (C) 2005 Juan Antonio Martinez <jonsito@teleline.es>
4     Based on pcsc_scan tool by Ludovic Rousseau <ludovic.rousseau@free.fr>
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19     02110-1301, USA.
20 */
21 
22 /* $Id$ */
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <sys/wait.h>
32 #include <fcntl.h>
33 
34 #include <pcsclite.h>
35 #include <wintypes.h>
36 #include <winscard.h>
37 
38 #include "config.h"
39 #include "../scconf/scconf.h"
40 #include "../common/debug.h"
41 #include "../common/error.h"
42 
43 #ifndef HAVE_DAEMON
44 int daemon(int nochdir, int noclose);
45 #endif
46 
47 #ifndef TRUE
48 #define TRUE 1
49 #define FALSE 0
50 #endif
51 
52 #define DEF_TIMEOUT 1000    /* 1 second timeout */
53 #define DEF_CONFIG_FILE CONFDIR "/card_eventmgr.conf"
54 
55 #define ONERROR_IGNORE	0
56 #define ONERROR_RETURN	1
57 #define ONERROR_QUIT	2
58 
59 int timeout;
60 int timeout_count;
61 int timeout_limit;
62 int daemonize;
63 int debug;
64 const char *cfgfile;
65 scconf_context *ctx = NULL;
66 const scconf_block *root;
67 SCARDCONTEXT hContext;
68 char *pidfile = NULL;
69 char AraKiri = FALSE;
70 
thats_all_folks(void)71 static void thats_all_folks(void) {
72     int rv;
73     DBG("Exitting");
74     /* We try to leave things as clean as possible */
75     rv = SCardReleaseContext(hContext);
76     if (rv != SCARD_S_SUCCESS) {
77         DBG1("SCardReleaseContext: %X", rv);
78     }
79 
80     /* free configuration context */
81     if (ctx)
82 	scconf_free(ctx);
83 }
84 
85 extern char **environ;
my_system(char * command)86 static int my_system(char *command) {
87 	int pid, status;
88 	   if (!command) return 1;
89            pid = fork();
90            if (pid == -1) return -1;
91            if (pid == 0) {
92                char *argv[4];
93                argv[0] = "/bin/sh";
94                argv[1] = "-c";
95                argv[2] = command;
96                argv[3] = 0;
97                execve("/bin/sh", argv, environ);
98                exit(127);
99            }
100            do {
101                if (waitpid(pid, &status, 0) == -1) {
102                    if (errno != EINTR) return -1;
103                } else return status;
104            } while(1);
105 }
106 
execute_event(const char * action)107 static int execute_event (const char *action) {
108 	int onerr;
109 	const char *onerrorstr;
110 	const scconf_list *actionlist;
111 	scconf_block **blocklist, *myblock;
112 	blocklist = scconf_find_blocks(ctx,root,"event",action);
113         if (!blocklist) {
114                 DBG("Event block list not found");
115 	        return -1;
116 	}
117 	myblock=blocklist[0];
118 	free(blocklist);
119 	blocklist = NULL;
120 	if (!myblock) {
121 		DBG1("Event item not found: '%s'",action);
122 		return -1;
123 	}
124 	onerrorstr = scconf_get_str(myblock,"on_error","ignore");
125 	if(!strcmp(onerrorstr,"ignore")) onerr = ONERROR_IGNORE;
126 	else if(!strcmp(onerrorstr,"return")) onerr = ONERROR_RETURN;
127 	else if(!strcmp(onerrorstr,"quit")) onerr = ONERROR_QUIT;
128 	else {
129 	    onerr = ONERROR_IGNORE;
130 	    DBG1("Invalid onerror value: '%s'. Assumed 'ignore'",onerrorstr);
131 	}
132 	/* search actions */
133 	actionlist = scconf_find_list(myblock,"action");
134 	if (!actionlist) {
135 	        DBG1("No action list for event '%s'",action);
136 		return 0;
137 	}
138 	DBG1("Onerror is set to: '%s'",onerrorstr);
139 	while (actionlist) {
140 		int res;
141 		char *action_cmd= actionlist->data;
142 		DBG1("Executiong action: '%s'",action_cmd);
143 		/*
144 		there are some security issues on using system() in
145 		setuid/setgid programs. so we will use an alternate function
146                 */
147 		/* res=system(action_cmd); */
148 		res = my_system(action_cmd);
149 		actionlist=actionlist->next;
150 		/* evaluate return and take care on "onerror" value */
151 		DBG2("Action '%s' returns %d",action_cmd, res);
152 		if (!res) continue;
153 		switch(onerr) {
154 		    case ONERROR_IGNORE: continue;
155 		    case ONERROR_RETURN: return 0;
156 		    case ONERROR_QUIT: 	thats_all_folks();
157 					exit(0);
158 		    default: 		DBG("Invalid onerror value");
159 			     		return -1;
160 		}
161 	}
162 	return 0;
163 }
164 
parse_config_file(void)165 static int parse_config_file(void) {
166         ctx = scconf_new(cfgfile);
167         if (!ctx) {
168            DBG("Error creating conf context");
169            return -1;
170         }
171         if ( scconf_parse(ctx) <= 0 ) {
172            DBG2("Error parsing file '%s': %s",cfgfile, ctx->errmsg);
173            return -1;
174         }
175         /* now parse options */
176         root = scconf_find_block(ctx, NULL, "card_eventmgr");
177         if (!root) {
178            DBG1("card_eventmgr block not found in config: '%s'",cfgfile);
179            return -1;
180         }
181 	debug = scconf_get_bool(root,"debug",debug);
182 	daemonize = scconf_get_bool(root,"daemon",daemonize);
183 	timeout = scconf_get_int(root,"timeout",timeout);
184 	timeout_limit = scconf_get_int(root,"timeout_limit",0);
185 	if (debug) set_debug_level(1);
186 	return 0;
187 }
188 
parse_args(int argc,char * argv[])189 static int parse_args(int argc, char *argv[]) {
190 	int i;
191 	timeout = DEF_TIMEOUT;
192 	timeout_limit = 0;
193 	debug   = 0;
194 	daemonize  = 0;
195 	cfgfile = DEF_CONFIG_FILE;
196         /* first of all check whether debugging should be enabled */
197         for (i = 0; i < argc; i++) {
198           if (! strcmp("debug", argv[i])) set_debug_level(1);
199         }
200         /* try to find a configuration file entry */
201         for (i = 0; i < argc; i++) {
202             if (strstr(argv[i],"config_file=") ) {
203                 cfgfile=1+strchr(argv[i],'=');
204                 break;
205             }
206         }
207 	/* parse configuration file */
208 	if ( parse_config_file()<0) {
209 		fprintf(stderr,"Error parsing configuration file %s\n",cfgfile);
210 		exit(-1);
211 	}
212 
213 	/* and now re-parse command line to take precedence over cfgfile */
214         for (i = 1; i < argc; i++) {
215             if (strcmp("daemon", argv[i]) == 0) {
216 		daemonize=1;
217 	  	continue;
218 	    }
219             if (strcmp("nodaemon", argv[i]) == 0) {
220 		daemonize=0;
221 	  	continue;
222 	    }
223             if (strcmp("kill", argv[i]) == 0) {
224 		AraKiri=TRUE;
225 	  	continue;
226 	    }
227             if (strstr(argv[i],"timeout=") ) {
228                 sscanf(argv[i],"timeout=%d",&timeout);
229                 continue;
230             }
231             if (strstr(argv[i],"timeout_limit=") ) {
232                 sscanf(argv[i],"timeout_limit=%d",&timeout_limit);
233                 continue;
234             }
235 	    if (strstr(argv[i],"pidfile=") ) {
236 		 pidfile = strchr(argv[i],'=') +1;
237 		continue;
238 	    }
239             if (strstr(argv[i],"debug") ) {
240 		continue;  /* already parsed: skip */
241 	    }
242             if (strstr(argv[i],"nodebug") ) {
243 		continue;  /* already parsed: skip */
244 	    }
245             if (strstr(argv[i],"config_file=") ) {
246 		continue; /* already parsed: skip */
247 	    }
248 	    fprintf(stderr,"unknown option %s\n",argv[i]);
249 	    /* arriving here means syntax error */
250 	    fprintf(stderr,"Usage %s [[no]debug] [[no]daemon] [timeout=<timeout>] [timeout_limit=<limit>] [config_file=<file>] [kill] [pidfile=<file>]\n",argv[0]);
251 	    fprintf(stderr,"Defaults: debug=0 daemon=0 timeout=%d (ms) timeout_limit=0 (none) config_file=%s\n",DEF_TIMEOUT,DEF_CONFIG_FILE );
252 	    exit(1);
253         } /* for */
254 	/* end of config: return */
255 	return 0;
256 }
257 
read_pidfile(char * filename)258 static pid_t read_pidfile(char *filename)
259 {
260     FILE *fd;
261     pid_t pid;
262     long temp = 0;
263 
264     fd = fopen(filename, "r");
265     if (NULL == fd)
266     {
267 	DBG2("Can't read pidfile %s: %s", filename, strerror(errno));
268 	return 0;
269     }
270 
271     if (fscanf(fd, "%ld", &temp) != 1)
272         DBG2("Can't parse pidfile %s: %s", filename, strerror(errno));
273 
274     pid = temp;
275 
276     fclose(fd);
277 
278     return pid;
279 }
280 
remove_pidfile(char * filename)281 static void remove_pidfile(char *filename)
282 {
283     if (unlink(filename))
284 	DBG2("Can't unlink pidfile %s: %s", filename, strerror(errno));
285 }
286 
create_pidfile(char * filename)287 static void create_pidfile(char *filename)
288 {
289     int fd;
290     char tmp[20];
291 
292     fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644);
293     if (fd < 0)
294     {
295 	DBG2("Can't create pidfile %s: %s", filename, strerror(errno));
296 	return;
297     }
298 
299     snprintf(tmp, sizeof(tmp)-1, "%d\n", getpid());
300     tmp[sizeof(tmp)-1] = '\0';
301 
302     if (write(fd, tmp, strlen(tmp)) != strlen(tmp))
303         DBG2("Can't write pidfile %s: %s", filename, strerror(errno));
304 
305     close(fd);
306 }
307 
signal_trap(int sig)308 static void signal_trap(int sig)
309 {
310     (void)sig;
311     if (FALSE == AraKiri)
312     {
313 	DBG("Preparing to suicide");
314 	AraKiri = TRUE;
315     }
316 }
317 
main(int argc,char * argv[])318 int main(int argc, char *argv[]) {
319     int current_reader;
320     LONG rv;
321     SCARD_READERSTATE *rgReaderStates_t = NULL;
322     DWORD dwReaders, dwReadersOld;
323     LPSTR mszReaders = NULL;
324     char *ptr, **readers = NULL;
325     int nbReaders, i;
326     int first_loop = TRUE;
327 
328     parse_args(argc,argv);
329 
330     /* AraKiri is set if kill argument is passed */
331     if (AraKiri)
332     {
333 	/* we are asked to kill the previous pkcs11_eventmgr */
334 	if (pidfile)
335 	{
336 	    pid_t pid = read_pidfile(pidfile);
337 	    if (pid > 0)
338 	    {
339 		DBG1("Killing process: %ld", (long)pid);
340 	    	kill(pid, SIGQUIT);
341 	    }
342 	    else
343 		DBG("Invalid pid");
344 	}
345 	else
346 	    DBG("You need to specify a pidfile");
347 	return 0;
348     }
349 
350     /* put my self into background if flag is set */
351     if (daemonize) {
352 	DBG("Going to be daemon...");
353 	if ( daemon(0,debug)<0 ) {
354 		DBG1("Error in daemon() call: %s", strerror(errno));
355 		goto end;
356 	}
357     }
358 
359     /* establish pc/sc handle _after_ possible fork */
360     rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
361     if (rv != SCARD_S_SUCCESS) {
362         DBG1("SCardEstablishContext: Cannot Connect to Resource Manager %lX", rv);
363 	if (ctx)
364 	    scconf_free(ctx);
365         return 1;
366     }
367 
368     signal(SIGINT, signal_trap);
369     signal(SIGQUIT, signal_trap);
370     signal(SIGTERM, signal_trap);
371 
372     if (pidfile)
373 	create_pidfile(pidfile);
374 
375 get_readers:
376     /* free memory possibly allocated in a previous loop */
377     /* free() already check if pt is null, so no check needed */
378     free(readers);
379     readers = NULL;
380     free(rgReaderStates_t);
381     rgReaderStates_t = NULL;
382 
383     /* Retrieve the available readers list.
384      *
385      * 1. Call with a null buffer to get the number of bytes to allocate
386      * 2. malloc the necessary storage
387      * 3. call with the real allocated buffer
388      */
389     DBG("Scanning present readers");
390     rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
391     if (rv != SCARD_S_SUCCESS) {
392         DBG1("SCardListReader: %lX", rv);
393     }
394     dwReadersOld = dwReaders;
395 
396     /* if non NULL we came back so free first */
397     free(mszReaders);
398     mszReaders = NULL;
399 
400     mszReaders = malloc(sizeof(char)*dwReaders);
401     if (mszReaders == NULL) {
402         DBG("malloc: not enough memory");
403         goto end;
404     }
405 
406     rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
407     if (rv != SCARD_S_SUCCESS) {
408         DBG1("SCardListReader: %lX", rv);
409     }
410 
411     /* Extract readers from the null separated string and get the total
412      * number of readers */
413     nbReaders = 0;
414     ptr = mszReaders;
415     while (*ptr != '\0') {
416         ptr += strlen(ptr)+1;
417         nbReaders++;
418     }
419 
420     if (nbReaders == 0) {
421     	/* exit if no reader is present at startup */
422 	if (first_loop) {
423 	    printf("%s: No reader present, exiting\n", argv[0]);
424 	    goto end;
425 	}
426 
427         DBG("Waiting for the first reader...");
428         while ((SCardListReaders(hContext, NULL, NULL, &dwReaders)
429             == SCARD_S_SUCCESS) && (dwReaders == dwReadersOld))
430             sleep(1);
431         DBG("found one");
432         goto get_readers;
433     }
434 
435     /* allocate the readers table */
436     readers = calloc(nbReaders, sizeof(char *));
437     if (! readers) {
438         DBG("Not enough memory for readers table");
439         goto end;
440     }
441 
442     /* fill the readers table */
443     nbReaders = 0;
444     ptr = mszReaders;
445     while (*ptr != '\0') {
446         DBG2("%d: %s", nbReaders, ptr);
447         readers[nbReaders] = ptr;
448         ptr += strlen(ptr)+1;
449         nbReaders++;
450     }
451 
452     /* allocate the ReaderStates table */
453     rgReaderStates_t = calloc(nbReaders, sizeof(* rgReaderStates_t));
454     if (! rgReaderStates_t) {
455         DBG("Not enough memory for readers states");
456         goto end;
457     }
458 
459     /* Set the initial states to something we do not know
460      * The loop below will include this state to the dwCurrentState
461      */
462     for (i=0; i<nbReaders; i++) {
463         rgReaderStates_t[i].szReader = readers[i];
464         rgReaderStates_t[i].dwCurrentState = SCARD_STATE_UNAWARE;
465     }
466 
467     /* Wait endlessly for all events in the list of readers
468      * We only stop in case of an error
469      */
470     rv = SCardGetStatusChange(hContext, timeout, rgReaderStates_t, nbReaders);
471     while ((rv == SCARD_S_SUCCESS) || (rv == SCARD_E_TIMEOUT)) {
472         /* A new reader appeared? */
473         if ((SCardListReaders(hContext, NULL, NULL, &dwReaders)
474             == SCARD_S_SUCCESS) && (dwReaders != dwReadersOld))
475                 goto get_readers;
476 
477 	   /* we were asked to suicide */
478 	   if (AraKiri)
479 		break;
480 
481         /* Now we have an event, check all the readers to see what happened */
482         for (current_reader=0; current_reader < nbReaders; current_reader++) {
483 	    unsigned long new_state;
484 
485             if (rgReaderStates_t[current_reader].dwEventState &
486                 SCARD_STATE_CHANGED) {
487                 /* If something has changed the new state is now the current
488                  * state */
489                 rgReaderStates_t[current_reader].dwCurrentState =
490                     rgReaderStates_t[current_reader].dwEventState;
491             }
492             /* If nothing changed then skip to the next reader */
493             else continue;
494 
495             /* From here we know that the state for the current reader has
496              * changed because we did not pass through the continue statement
497              * above.
498              */
499 
500 	    if (first_loop)
501 		continue; /* skip first pass */
502 
503             /* Specify the current reader's number and name */
504             DBG2("Reader %d (%s)", current_reader,
505                 rgReaderStates_t[current_reader].szReader);
506 
507             /* Dump the full current state */
508 	    new_state = rgReaderStates_t[current_reader].dwEventState;
509             DBG1("Card state: 0x%08ld", new_state);
510 
511             if (new_state & SCARD_STATE_UNKNOWN) {
512                 DBG("Reader unknown");
513                 goto get_readers;
514             }
515 
516             if (new_state & SCARD_STATE_EMPTY) {
517                     DBG("Card removed");
518 		    execute_event("card_remove");
519             }
520 
521             if (new_state & SCARD_STATE_PRESENT) {
522                     DBG("Card inserted");
523 		    execute_event("card_insert");
524             }
525         } /* for */
526 
527 	first_loop = FALSE;
528         rv = SCardGetStatusChange(hContext, timeout, rgReaderStates_t, nbReaders);
529     } /* while */
530 
531     /* If we get out the loop, GetStatusChange() was unsuccessful */
532     DBG1("SCardGetStatusChange: %lX", rv);
533 
534 end:
535     /* free memory possibly allocated */
536     free(readers);
537     readers = NULL;
538     free(rgReaderStates_t);
539     rgReaderStates_t = NULL;
540 
541     if (pidfile)
542 	remove_pidfile(pidfile);
543 
544     thats_all_folks();
545     exit(0);
546 } /* main */
547 
548