1 /*
2  * MUSCLE SmartCard Development ( https://pcsclite.apdu.fr/ )
3  *
4  * Copyright (C) 2001-2003
5  *  David Corcoran <corcoran@musclecard.com>
6  * Copyright (C) 2002-2011
7  *  Ludovic Rousseau <ludovic.rousseau@free.fr>
8  *
9  * The USB code was based partly on Johannes Erdfelt
10  * libusb code found at libusb.sourceforge.net
11  *
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions
14 are met:
15 
16 1. Redistributions of source code must retain the above copyright
17    notice, this list of conditions and the following disclaimer.
18 2. Redistributions in binary form must reproduce the above copyright
19    notice, this list of conditions and the following disclaimer in the
20    documentation and/or other materials provided with the distribution.
21 3. The name of the author may not be used to endorse or promote products
22    derived from this software without specific prior written permission.
23 
24 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 /**
37  * @file
38  * @brief This provides a search API for hot pluggble devices.
39  */
40 
41 #include "config.h"
42 #include <string.h>
43 
44 #if defined(__linux__) && !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUDEV)
45 #include <sys/types.h>
46 #include <stdio.h>
47 #include <dirent.h>
48 #include <fcntl.h>
49 #include <time.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <pthread.h>
53 
54 #include "misc.h"
55 #include "pcsclite.h"
56 #include "pcscd.h"
57 #include "debuglog.h"
58 #include "parser.h"
59 #include "readerfactory.h"
60 #include "winscard_msg.h"
61 #include "sys_generic.h"
62 #include "hotplug.h"
63 #include "utils.h"
64 
65 #undef DEBUG_HOTPLUG
66 #define PCSCLITE_USB_PATH		"/proc/bus/usb"
67 
68 #define FALSE			0
69 #define TRUE			1
70 
71 pthread_mutex_t usbNotifierMutex;
72 
73 struct usb_device_descriptor
74 {
75 	u_int8_t bLength;
76 	u_int8_t bDescriptorType;
77 	u_int16_t bcdUSB;
78 	u_int8_t bDeviceClass;
79 	u_int8_t bDeviceSubClass;
80 	u_int8_t bDeviceProtocol;
81 	u_int8_t bMaxPacketSize0;
82 	u_int16_t idVendor;
83 	u_int16_t idProduct;
84 	u_int16_t bcdDevice;
85 	u_int8_t iManufacturer;
86 	u_int8_t iProduct;
87 	u_int8_t iSerialNumber;
88 	u_int8_t bNumConfigurations;
89 }
90 __attribute__ ((packed));
91 
92 static LONG HPAddHotPluggable(int, unsigned long);
93 static LONG HPRemoveHotPluggable(int, unsigned long);
94 static LONG HPReadBundleValues(void);
95 static void HPEstablishUSBNotifications(void);
96 
97 static pthread_t usbNotifyThread;
98 static int AraKiriHotPlug = FALSE;
99 static int bundleSize = 0;
100 
101 /**
102  * A list to keep track of 20 simultaneous readers
103  */
104 static struct _bundleTracker
105 {
106 	long  manuID;
107 	long  productID;
108 
109 	struct _deviceNumber {
110 		int  id;
111 		char status;
112 	} deviceNumber[PCSCLITE_MAX_READERS_CONTEXTS];
113 
114 	char *bundleName;
115 	char *libraryPath;
116 	char *readerName;
117 }
118 bundleTracker[PCSCLITE_MAX_READERS_CONTEXTS];
119 
HPReadBundleValues(void)120 static LONG HPReadBundleValues(void)
121 {
122 	LONG rv;
123 	DIR *hpDir;
124 	struct dirent *currFP = 0;
125 	char fullPath[FILENAME_MAX];
126 	char fullLibPath[FILENAME_MAX];
127 	unsigned int listCount = 0;
128 
129 	hpDir = opendir(PCSCLITE_HP_DROPDIR);
130 
131 	if (hpDir == NULL)
132 	{
133 		Log1(PCSC_LOG_INFO,
134 			"Cannot open PC/SC drivers directory: " PCSCLITE_HP_DROPDIR);
135 		Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd.");
136 		return -1;
137 	}
138 
139 #define GET_KEY(key, values) \
140 	rv = LTPBundleFindValueWithKey(&plist, key, values); \
141 	if (rv) \
142 	{ \
143 		Log2(PCSC_LOG_ERROR, "Value/Key not defined for " key " in %s", \
144 			fullPath); \
145 		continue; \
146 	}
147 
148 	while ((currFP = readdir(hpDir)) != 0)
149 	{
150 		if (strstr(currFP->d_name, ".bundle") != 0)
151 		{
152 			unsigned int alias;
153 			list_t plist, *values;
154 			list_t *manuIDs, *productIDs, *readerNames;
155 			char *libraryPath;
156 
157 			/*
158 			 * The bundle exists - let's form a full path name and get the
159 			 * vendor and product ID's for this particular bundle
160 			 */
161 			snprintf(fullPath, FILENAME_MAX, "%s/%s/Contents/Info.plist",
162 				PCSCLITE_HP_DROPDIR, currFP->d_name);
163 			fullPath[FILENAME_MAX - 1] = '\0';
164 
165 			rv = bundleParse(fullPath, &plist);
166 			if (rv)
167 				continue;
168 
169 			/* get CFBundleExecutable */
170 			GET_KEY(PCSCLITE_HP_LIBRKEY_NAME, &values)
171 			libraryPath = list_get_at(values, 0);
172 			(void)snprintf(fullLibPath, sizeof(fullLibPath),
173 				"%s/%s/Contents/%s/%s",
174 				PCSCLITE_HP_DROPDIR, currFP->d_name, PCSC_ARCH,
175 				libraryPath);
176 			fullLibPath[sizeof(fullLibPath) - 1] = '\0';
177 
178 			GET_KEY(PCSCLITE_HP_CPCTKEY_NAME, &values)
179 			GET_KEY(PCSCLITE_HP_MANUKEY_NAME, &manuIDs)
180 			GET_KEY(PCSCLITE_HP_PRODKEY_NAME, &productIDs)
181 			GET_KEY(PCSCLITE_HP_NAMEKEY_NAME, &readerNames)
182 
183 			/* while we find a nth ifdVendorID in Info.plist */
184 			for (alias=0; alias<list_size(manuIDs); alias++)
185 			{
186 				char *value;
187 
188 				/* variables entries */
189 				value = list_get_at(manuIDs, alias);
190 				bundleTracker[listCount].manuID = strtol(value, NULL, 16);
191 
192 				value = list_get_at(productIDs, alias);
193 				bundleTracker[listCount].productID = strtol(value, NULL, 16);
194 
195 				bundleTracker[listCount].readerName = strdup(list_get_at(readerNames, alias));
196 
197 				/* constant entries for a same driver */
198 				bundleTracker[listCount].bundleName = strdup(currFP->d_name);
199 				bundleTracker[listCount].libraryPath = strdup(fullLibPath);
200 
201 #ifdef DEBUG_HOTPLUG
202 				Log2(PCSC_LOG_INFO, "Found driver for: %s",
203 					bundleTracker[listCount].readerName);
204 #endif
205 				listCount++;
206 
207 				if (listCount >= COUNT_OF(bundleTracker))
208 				{
209 					Log2(PCSC_LOG_CRITICAL, "Too many readers declared. Maximum is %zd", COUNT_OF(bundleTracker));
210 					goto end;
211 				}
212 			}
213 			bundleRelease(&plist);
214 		}
215 	}
216 
217 end:
218 	bundleSize = listCount;
219 
220 	if (bundleSize == 0)
221 	{
222 		Log1(PCSC_LOG_INFO,
223 			"No bundle files in pcsc drivers directory: " PCSCLITE_HP_DROPDIR);
224 		Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd");
225 	}
226 
227 	closedir(hpDir);
228 	return bundleSize;
229 }
230 
HPEstablishUSBNotifications(void)231 static void HPEstablishUSBNotifications(void)
232 {
233 
234 	int i, j, usbDeviceStatus;
235 	DIR *dir, *dirB;
236 	struct dirent *entry, *entryB;
237 	int deviceNumber;
238 	int suspectDeviceNumber;
239 	char dirpath[FILENAME_MAX];
240 	char filename[FILENAME_MAX * 2];
241 	int fd, ret;
242 	struct usb_device_descriptor usbDescriptor;
243 
244 	usbDeviceStatus = 0;
245 	suspectDeviceNumber = 0;
246 
247 	while (1)
248 	{
249 		for (i = 0; i < bundleSize; i++)
250 		{
251 			usbDeviceStatus     = 0;
252 			suspectDeviceNumber = 0;
253 
254 			for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
255 				/* clear rollcall */
256 				bundleTracker[i].deviceNumber[j].status = 0;
257 
258 			dir = NULL;
259 			dir = opendir(PCSCLITE_USB_PATH);
260 			if (dir == NULL)
261 			{
262 				Log1(PCSC_LOG_ERROR,
263 					"Cannot open USB path directory: " PCSCLITE_USB_PATH);
264 				return;
265 			}
266 
267 			entry = NULL;
268 			while ((entry = readdir(dir)) != 0)
269 			{
270 
271 				/*
272 				 * Skip anything starting with a
273 				 */
274 				if (entry->d_name[0] == '.')
275 					continue;
276 				if (!strchr("0123456789",
277 						entry->d_name[strlen(entry->d_name) - 1]))
278 				{
279 					continue;
280 				}
281 
282 				snprintf(dirpath, sizeof dirpath, "%s/%s",
283 					PCSCLITE_USB_PATH, entry->d_name);
284 
285 				dirB = opendir(dirpath);
286 
287 				if (dirB == NULL)
288 				{
289 					Log2(PCSC_LOG_ERROR,
290 						"USB path seems to have disappeared %s", dirpath);
291 					closedir(dir);
292 					return;
293 				}
294 
295 				while ((entryB = readdir(dirB)) != NULL)
296 				{
297 					/*
298 					 * Skip anything starting with a
299 					 */
300 					if (entryB->d_name[0] == '.')
301 						continue;
302 
303 					/* Get the device number so we can distinguish
304 					   multiple readers */
305 					snprintf(filename, sizeof filename, "%s/%s",
306 						dirpath, entryB->d_name);
307 					deviceNumber = atoi(entryB->d_name);
308 
309 					fd = open(filename, O_RDONLY);
310 					if (fd < 0)
311 						continue;
312 
313 					ret = read(fd, (void *) &usbDescriptor,
314 						sizeof(usbDescriptor));
315 
316 					close(fd);
317 
318 					if (ret < 0)
319 						continue;
320 
321 					/*
322 					 * Device is found and we don't know about it
323 					 */
324 
325 					if (usbDescriptor.idVendor == bundleTracker[i].manuID &&
326 						usbDescriptor.idProduct == bundleTracker[i].productID &&
327 						usbDescriptor.idVendor !=0 &&
328 						usbDescriptor.idProduct != 0)
329 					{
330 						for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
331 						{
332 							if (bundleTracker[i].deviceNumber[j].id == deviceNumber &&
333 								bundleTracker[i].deviceNumber[j].id != 0)
334 							{
335 								bundleTracker[i].deviceNumber[j].status = 1; /* i'm here */
336 								break;
337 							}
338 						}
339 
340 						if (j == PCSCLITE_MAX_READERS_CONTEXTS)
341 						{
342 							usbDeviceStatus = 1;
343 							suspectDeviceNumber = deviceNumber;
344 						}
345 					}
346 
347 				} /* End of while */
348 
349 				closedir(dirB);
350 
351 			} /* End of while */
352 
353 
354 			if (usbDeviceStatus == 1)
355 			{
356 				pthread_mutex_lock(&usbNotifierMutex);
357 
358 				for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
359 				{
360 					if (bundleTracker[i].deviceNumber[j].id == 0)
361 						break;
362 				}
363 
364 				if (j == PCSCLITE_MAX_READERS_CONTEXTS)
365 					Log1(PCSC_LOG_ERROR,
366 						"Too many identical readers plugged in");
367 				else
368 				{
369 					HPAddHotPluggable(i, j+1);
370 					bundleTracker[i].deviceNumber[j].id = suspectDeviceNumber;
371 				}
372 
373 				pthread_mutex_unlock(&usbNotifierMutex);
374 			}
375 			else
376 				if (usbDeviceStatus == 0)
377 				{
378 
379 					for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
380 					{
381 						if (bundleTracker[i].deviceNumber[j].id != 0 &&
382 							bundleTracker[i].deviceNumber[j].status == 0)
383 						{
384 							pthread_mutex_lock(&usbNotifierMutex);
385 							HPRemoveHotPluggable(i, j+1);
386 							bundleTracker[i].deviceNumber[j].id = 0;
387 							pthread_mutex_unlock(&usbNotifierMutex);
388 						}
389 					}
390 				}
391 				else
392 				{
393 					/*
394 					 * Do nothing - no USB devices found
395 					 */
396 				}
397 
398 			if (dir)
399 				closedir(dir);
400 
401 		}	/* End of for..loop */
402 
403 		SYS_Sleep(1);
404 		if (AraKiriHotPlug)
405 		{
406 			int retval;
407 
408 			Log1(PCSC_LOG_INFO, "Hotplug stopped");
409 			pthread_exit(&retval);
410 		}
411 
412 	}	/* End of while loop */
413 }
414 
HPSearchHotPluggables(void)415 LONG HPSearchHotPluggables(void)
416 {
417 	int i, j;
418 
419 	for (i = 0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++)
420 	{
421 		bundleTracker[i].productID  = 0;
422 		bundleTracker[i].manuID     = 0;
423 
424 		for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
425 			bundleTracker[i].deviceNumber[j].id = 0;
426 	}
427 
428 	if (HPReadBundleValues() > 0)
429 		ThreadCreate(&usbNotifyThread, THREAD_ATTR_DETACHED,
430 			(PCSCLITE_THREAD_FUNCTION( )) HPEstablishUSBNotifications, 0);
431 
432 	return 0;
433 }
434 
HPStopHotPluggables(void)435 LONG HPStopHotPluggables(void)
436 {
437 	AraKiriHotPlug = TRUE;
438 
439 	return 0;
440 }
441 
HPAddHotPluggable(int i,unsigned long usbAddr)442 static LONG HPAddHotPluggable(int i, unsigned long usbAddr)
443 {
444 	/* NOTE: The deviceName is an empty string "" until someone implements
445 	 * the code to get it */
446 	RFAddReader(bundleTracker[i].readerName, PCSCLITE_HP_BASE_PORT + usbAddr,
447 		bundleTracker[i].libraryPath, "");
448 
449 	return 1;
450 }	/* End of function */
451 
HPRemoveHotPluggable(int i,unsigned long usbAddr)452 static LONG HPRemoveHotPluggable(int i, unsigned long usbAddr)
453 {
454 	RFRemoveReader(bundleTracker[i].readerName, PCSCLITE_HP_BASE_PORT + usbAddr);
455 
456 	return 1;
457 }	/* End of function */
458 
459 /**
460  * Sets up callbacks for device hotplug events.
461  */
HPRegisterForHotplugEvents(void)462 ULONG HPRegisterForHotplugEvents(void)
463 {
464 	(void)pthread_mutex_init(&usbNotifierMutex, NULL);
465 	return 0;
466 }
467 
HPReCheckSerialReaders(void)468 void HPReCheckSerialReaders(void)
469 {
470 }
471 
472 #endif	/* __linux__ && !HAVE_LIBUSB */
473