1 /* $Id: sectok.c,v 1.12 2003/04/04 00:50:56 deraadt Exp $ */
2 
3 /*
4 copyright 2000
5 the regents of the university of michigan
6 all rights reserved
7 
8 permission is granted to use, copy, create derivative works
9 and redistribute this software and such derivative works
10 for any purpose, so long as the name of the university of
11 michigan is not used in any advertising or publicity
12 pertaining to the use or distribution of this software
13 without specific, written prior authorization.  if the
14 above copyright notice or any other identification of the
15 university of michigan is included in any copy of any
16 portion of this software, then the disclaimer below must
17 also be included.
18 
19 this software is provided as is, without representation
20 from the university of michigan as to its fitness for any
21 purpose, and without warranty by the university of
22 michigan of any kind, either express or implied, including
23 without limitation the implied warranties of
24 merchantability and fitness for a particular purpose. the
25 regents of the university of michigan shall not be liable
26 for any damages, including special, indirect, incidental, or
27 consequential damages, with respect to any claim arising
28 out of or in connection with the use of the software, even
29 if it has been or is hereafter advised of the possibility of
30 such damages.
31 */
32 
33 /*
34  * common card routines
35  *
36  * Jim Rees
37  * University of Michigan CITI, July 2001
38  */
39 
40 #ifdef __palmos__
41 #include <Common.h>
42 #include <System/SysAll.h>
43 #include <System/Unix/sys_types.h>
44 #include <System/Unix/unix_stdlib.h>
45 #include <System/Unix/unix_string.h>
46 #include <UI/UIAll.h>
47 #include "field.h"
48 #undef open
49 #else
50 #include <sys/types.h>
51 #include <sys/time.h>
52 #include <stdlib.h>
53 #include <unistd.h>
54 #include <stdio.h>
55 #include <string.h>
56 #include <dlfcn.h>
57 #include <errno.h>
58 #endif
59 
60 #include "sectok.h"
61 #include "ifdhandler.h"
62 
63 #define MAX_READERS 32
64 #define N_DEFAULT_READERS 4
65 
66 #define myisprint(x) ((x) >= '!' && (x) <= 'z')
67 
68 #ifdef DL_READERS
69 static char defaultConfigFilePath[] = "/etc/reader.conf";
70 static char defaultDriverPath[] = "/usr/local/pcsc/lib/libtodos_ag.so";
71 
72 int DBUpdateReaders(char *readerconf, int (callback) (int rn, unsigned long channelId, char *driverFile));
73 
74 /* the callback for DBUpdateReaders */
75 static int addReader(int rn, unsigned long channelID, char *driverFile);
76 static void *lookupSym(void *handle, char *name);
77 #else
78 static int sillyports[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
79 #endif
80 
81 static int openReader(int readerNum, int flags);
82 
83 typedef struct {
84     unsigned long 	channelID;
85     char 		*driverPath;
86     unsigned int	driverLoaded;
87     /* for now, i'm only worry about the "bare essentials" */
88     u_long		(*open)(unsigned long channelID);
89     u_long		(*close)(void);
90     u_long		(*data)(struct SCARD_IO_HEADER junk,
91 				unsigned char *cmdData, unsigned long cmdLen,
92 				unsigned char *respData, unsigned long *respLen,
93 				struct SCARD_IO_HEADER *moreJunk);
94     u_long		(*power)(unsigned long command);
95     u_long		(*getcapa)(unsigned long selector, unsigned char *buffer);
96     u_long		(*setcapa)(unsigned long selector, unsigned char *buffer);
97     u_long		(*cardpresent)(void);
98 } readerInfo;
99 
100 unsigned int numReaders;
101 readerInfo readers[MAX_READERS];
102 
103 unsigned char root_fid[] = {0x3f, 0x00};
104 
105 /*
106   if (config_path != NULL and driver_name != NULL) error;
107   if (config_path != NULL) use reader.conf there;
108   if (driver_path != NULL) use the specified driver;
109   if (config_path == NULL and driver_path == NULL) use /etc/reader.conf;
110 
111   Note that the config file is read only once, and drivers are only loaded once,
112   so config_path and driver_path are ignored on subsequent calls.
113 */
114 
115 int
sectok_xopen(int rn,int flags,char * config_path,char * driver_path,int * swp)116 sectok_xopen(int rn, int flags, char *config_path, char *driver_path, int *swp)
117 {
118     int r = 0;
119 
120 #ifdef SCPERF
121     SetTime ("scopen() start");
122 #endif /* SCPERF */
123 
124     if (rn < 0 || rn >= MAX_READERS) {
125 	r = STENOTTY;
126 	goto out;
127     }
128 
129 #ifdef DL_READERS
130     if (driver_path) {
131 	/* caller specified a particular driver path to use */
132 	if (config_path) {
133 	    /* but also specified a config file, which is an error. */
134 	    r = STECNFFILES;
135 	    goto out;
136 	}
137 	if (!readers[rn].driverPath) {
138 	    /* need a driver */
139 	    if (addReader(rn, (0x10000 + rn), driver_path) < 0) {
140 		r = STEDRVR;
141 		goto out;
142 	    }
143 	}
144     }
145 
146     if (numReaders == 0) {
147 	/* no drivers; read the config file */
148 	if (!config_path)
149 	    config_path = defaultConfigFilePath;
150 	if (DBUpdateReaders(config_path, addReader) < 0) {
151 	    int i;
152 
153 	    if (config_path != defaultConfigFilePath) {
154 		/* Something wrong with caller's config file path. */
155 		r = STEDRVR;
156 		goto out;
157 	    }
158 	    /* This usually means there is no reader.conf.  Supply defaults. */
159 	    for (i = 0; i < N_DEFAULT_READERS; i++)
160 		addReader(i, (0x10000 | i), defaultDriverPath);
161 	}
162     }
163 #else
164     numReaders = N_DEFAULT_READERS;
165 #endif
166 
167     r = openReader(rn, flags);
168 
169 #ifdef __palmos__
170     if (sectok_swOK(r) && !sectok_cardpresent(rn))
171 	r = STENOCARD;
172 #else
173     if (sectok_swOK(r) && !(flags & STONOWAIT)) {
174 	/* Wait for card present */
175 	while (!sectok_cardpresent(rn)) {
176 	    errno = 0;
177 	    sleep(1);
178 	    if (errno == EINTR) {
179 		r = STENOCARD;
180 		break;
181 	    }
182 	}
183     }
184 #endif
185 
186  out:
187 #ifdef SCPERF
188     SetTime ("scopen() end");
189 #endif /* SCPERF */
190 
191     if (swp)
192 	*swp = r;
193     return (!sectok_swOK(r) ? -1 : rn);
194 }
195 
sectok_open(int rn,int flags,int * swp)196 int sectok_open(int rn, int flags, int *swp)
197 {
198     return sectok_xopen(rn, flags, NULL, NULL, swp);
199 }
200 
sectok_friendly_open(const char * rn,int flags,int * swp)201 int sectok_friendly_open(const char *rn, int flags, int *swp)
202 {
203     /* just convert the reader to a integer for now */
204     if (rn != NULL) {
205         return sectok_xopen(atoi(rn), flags, NULL, NULL, swp);
206     } else {
207         return -1;
208     }
209 }
210 
211 static int
openReader(int readerNum,int flags)212 openReader(int readerNum, int flags)
213 {
214     readerInfo *reader;
215 
216 #ifdef DEBUG
217     printf("openReader %d\n", readerNum);
218 #endif
219 
220     if (readerNum < 0 || readerNum >= MAX_READERS)
221 	return STEDRVR;
222     reader = &readers[readerNum];
223 
224     if (!reader->driverLoaded) {
225 #ifdef DL_READERS
226 	void *libHandle;
227 
228 	if (!reader->driverPath)
229 	    return STEDRVR;
230 	libHandle = dlopen(reader->driverPath, RTLD_LAZY);
231 	if (!libHandle) {
232 #ifdef DEBUG
233 	    printf("%s: %s\n", reader->driverPath, dlerror());
234 #endif
235 	    return STEDRVR;
236 	}
237 	reader->open = lookupSym(libHandle, "IO_Create_Channel");
238 	if (reader->open == NULL)
239 	    return STEDRVR;
240 
241 	reader->close = lookupSym(libHandle, "IO_Close_Channel");
242 	if (reader->close == NULL)
243 	    return STEDRVR;
244 
245 	reader->data = lookupSym(libHandle, "IFD_Transmit_to_ICC");
246 	if (reader->data == NULL)
247 	    return STEDRVR;
248 
249 	reader->power = lookupSym(libHandle, "IFD_Power_ICC");
250 	if (reader->power == NULL)
251 	    return STEDRVR;
252 
253 	reader->getcapa = lookupSym(libHandle, "IFD_Get_Capabilities");
254 	if (reader->getcapa == NULL)
255 	    return STEDRVR;
256 
257 	reader->setcapa = lookupSym(libHandle, "IFD_Set_Capabilities");
258 	if (reader->setcapa == NULL)
259 	    return STEDRVR;
260 
261 	reader->cardpresent = lookupSym(libHandle, "IFD_Is_ICC_Present");
262 #else /* DL_READERS */
263 	reader->open = IO_Create_Channel;
264 	reader->close = IO_Close_Channel;
265 	reader->data = IFD_Transmit_to_ICC;
266 	reader->power = IFD_Power_ICC;
267 	reader->getcapa = IFD_Get_Capabilities;
268 	reader->setcapa = IFD_Set_Capabilities;
269 	reader->cardpresent = IFD_Is_ICC_Present;
270 	reader->channelID = (0x10000 | (readerNum < 4 ? sillyports[readerNum] : readerNum));
271 #endif /* DL_READERS */
272 
273 	reader->driverLoaded = 1;
274     }
275 
276     /* send flags to the driver */
277     flags ^= STONOWAIT;
278     reader->setcapa(SCTAG_OPEN_FLAGS, (u_char *)&flags);
279     /* if this returns an error, setcapa is not supported in this driver,
280        but that's OK. */
281 
282     if (reader->open(reader->channelID))
283 	return STECOMM;
284     else
285 	return STEOK;
286 }
287 
288 int
sectok_close(int fd)289 sectok_close(int fd)
290 {
291     readerInfo *reader = &readers[fd];
292 
293     if (fd < 0 || fd >= MAX_READERS)
294 	return -1;
295 
296     reader = &readers[fd];
297 
298     if (!reader->driverLoaded)
299 	return -1;
300 
301     return (reader->close()) ? -1 : 0;
302 }
303 
304 int
sectok_cardpresent(int fd)305 sectok_cardpresent(int fd)
306 {
307     readerInfo *reader = &readers[fd];
308     unsigned long v;
309 
310     if (!reader->driverLoaded)
311 	return 0;
312 
313     if (reader->cardpresent)
314 	v = reader->cardpresent();
315     else if (reader->getcapa(SCTAG_IFD_CARDPRESENT, (unsigned char *) &v))
316 	return 1;
317 
318     return (v == IFD_ICC_PRESENT || v == 0) ? 1 : 0;
319 }
320 
321 int
sectok_reset(int fd,int flags,unsigned char * atr,int * swp)322 sectok_reset(int fd, int flags, unsigned char *atr, int *swp)
323 {
324     readerInfo *reader = &readers[fd];
325     int n = 0, r = STEOK;
326 
327 #ifdef SCPERF
328     SetTime ("scxreset() start");
329 #endif /* SCPERF */
330 
331     if (!reader->driverLoaded) {
332 	r = STECLOSED;
333 	goto out;
334     }
335 
336     if (!sectok_cardpresent(fd)) {
337 	r = STENOCARD;
338 	goto out;
339     }
340 
341     /* send flags to the driver */
342     reader->setcapa(SCTAG_RESET_FLAGS, (u_char *)&flags);
343     /* if this returns an error, setcapa is not supported in this driver,
344        but that's OK. */
345 
346     if (reader->power(IFD_RESET)) {
347 #ifdef DEBUG
348 	printf("power failed!\n");
349 #endif
350 	r = STESLAG;
351 	goto out;
352     }
353 
354     if (atr && reader->getcapa(TAG_IFD_ATR, atr)) {
355 #ifdef DEBUG
356 	printf("reset failed!\n");
357 #endif
358 	r = STESLAG;
359 	goto out;
360     }
361 
362     if (reader->getcapa(SCTAG_IFD_ATRLEN, (unsigned char *) &n) || n <= 0) {
363 	/* can't get atr len, take a wild guess */
364 	if (atr) {
365 	    for (n = MAX_ATR_SIZE - 1; !atr[n]; n--)
366 		;
367 	    n--;
368 	} else
369 	    n = MAX_ATR_SIZE;
370     }
371 
372  out:
373     if (swp)
374 	*swp = r;
375 
376 #ifdef SCPERF
377     SetTime ("scxreset() end");
378 #endif /* SCPERF */
379 
380     return n;
381 }
382 
383 #ifdef DL_READERS
384 static int
addReader(int rn,unsigned long channelID,char * driverFile)385 addReader(int rn, unsigned long channelID, char *driverFile)
386 {
387     readerInfo *reader;
388 
389     if (rn < 0 || rn >= MAX_READERS)
390 	return -1;
391 
392     reader = &readers[rn];
393 
394     if (reader->driverPath)
395 	return -1;
396 
397     reader->channelID = channelID;
398     reader->driverPath = strdup(driverFile);
399     reader->driverLoaded = 0;
400     numReaders++;
401     return 0;
402 }
403 
404 static void *
lookupSym(void * handle,char * name)405 lookupSym(void *handle, char *name)
406 {
407 #ifdef __linux__
408     return dlsym(handle, name);
409 #elif __sun
410     return dlsym(handle, name);
411 #else
412     char undername[32];
413 
414     snprintf(undername, sizeof undername, "_%s", name);
415     return dlsym(handle, undername);
416 #endif
417 }
418 #endif /* DL_READERS */
419 
420 int
sectok_apdu(int fd,int cla,int ins,int p1,int p2,int ilen,unsigned char * ibuf,int olen,unsigned char * obuf,int * swp)421 sectok_apdu(int fd, int cla, int ins, int p1, int p2,
422 	    int ilen, unsigned char *ibuf, int olen, unsigned char *obuf, int *swp)
423 {
424     unsigned char cmd[6+255], rsp[255+2];
425     unsigned long n;
426     int le;
427     readerInfo *reader = &readers[fd];
428     struct SCARD_IO_HEADER garbage;
429 
430     if (reader->driverLoaded == 0) {
431 	*swp = STECLOSED;
432 	return -1;
433     }
434 
435     cmd[0] = cla;
436     cmd[1] = ins;
437     cmd[2] = p1;
438     cmd[3] = p2;
439 
440     ilen &= 0xff;
441     le = (255 < olen) ? 255 : olen;
442 
443     if (ilen && ibuf) {
444 	/* Send "in" data */
445 	cmd[4] = ilen;
446 	memcpy(&cmd[5], ibuf, ilen);
447 	ilen += 5;
448 	if (le)
449 	    cmd[ilen++] = le;
450 	n = obuf ? sizeof rsp : 2;
451 	if (reader->data(garbage, cmd, ilen, rsp, &n, NULL) || n < 2) {
452 	    *swp = STECOMM;
453 	    return -1;
454 	}
455 	if (rsp[n-2] == 0x61 && olen && obuf) {
456 	    /* Response available; get it (driver should do this but some don't) */
457 	    cmd[1] = 0xc0;
458 	    cmd[2] = cmd[3] = 0;
459 	    cmd[4] = rsp[n-1];
460 	    n = sizeof rsp;
461 	    if (reader->data(garbage, cmd, 5, rsp, &n, NULL)) {
462 		*swp = STECOMM;
463 		return -1;
464 	    }
465 	}
466     } else {
467 	/* Get "out" data */
468 	cmd[4] = olen;
469 	n = sizeof rsp;
470 	if (reader->data(garbage, cmd, 5, rsp, &n, NULL)) {
471 	    *swp = STECOMM;
472 	    return -1;
473 	}
474     }
475 
476     if (n >= 2) {
477 	*swp = sectok_mksw(rsp[n-2], rsp[n-1]);
478 	n -= 2;
479     } else {
480 	/* This shouldn't happen; apdu ok but no status available */
481 	*swp = STEOK;
482     }
483 
484     if (n && olen)
485 	memcpy(obuf, rsp, (n < olen) ? n : olen);
486 
487     return n;
488 }
489 
490 void
sectok_fmt_fid(char * fname,size_t fnamelen,unsigned char * fid)491 sectok_fmt_fid(char *fname, size_t fnamelen, unsigned char *fid)
492 {
493     int f0 = fid[0], f1 = fid[1];
494 
495     if (f0 == 0x3f && f1 == 0)
496 	snprintf(fname, fnamelen, "/");
497     else if (myisprint(f0) && f1 == 0)
498 	snprintf(fname, fnamelen, "%c", f0);
499     else if (myisprint(f0) && myisprint(f1))
500 	snprintf(fname, fnamelen, "%c%c", f0, f1);
501     else
502 	snprintf(fname, fnamelen, "%02x%02x", f0, f1);
503 }
504 
505 int
sectok_selectfile(int fd,int cla,unsigned char * fid,int * swp)506 sectok_selectfile(int fd, int cla, unsigned char *fid, int *swp)
507 {
508     unsigned char obuf[256];
509 
510     sectok_apdu(fd, cla, 0xa4, 0, 0, 2, fid, sizeof obuf, obuf, swp);
511     if (!sectok_swOK(*swp))
512 	return -1;
513 
514     return 0;
515 }
516