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