diff options
Diffstat (limited to 'lib/libsectok/sectok.c')
-rw-r--r-- | lib/libsectok/sectok.c | 409 |
1 files changed, 404 insertions, 5 deletions
diff --git a/lib/libsectok/sectok.c b/lib/libsectok/sectok.c index f23e93c840c..5a760fc723c 100644 --- a/lib/libsectok/sectok.c +++ b/lib/libsectok/sectok.c @@ -1,4 +1,4 @@ -/* $Id: sectok.c,v 1.3 2001/06/28 21:37:28 rees Exp $ */ +/* $Id: sectok.c,v 1.4 2001/07/02 20:07:09 rees Exp $ */ /* copyright 2000 @@ -37,17 +37,417 @@ such damages. * University of Michigan CITI, July 2001 */ +#include <sys/types.h> +#include <sys/time.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> +#include <dlfcn.h> +#include <errno.h> #include "sectok.h" +#include "ifdhandler.h" + +#define MAX_READERS 32 +#define N_DEFAULT_READERS 4 #define myisprint(x) ((x) >= '!' && (x) <= 'z') +#ifdef DL_READERS +static char defaultConfigFilePath[] = "/etc/reader.conf"; +static char defaultDriverPath[] = "/usr/local/pcsc/lib/libtodos_ag.so"; + +int DBUpdateReaders(char *readerconf, int (callback) (int rn, unsigned long channelId, char *driverFile)); + +/* the callback for DBUpdateReaders */ +static int addReader(int rn, unsigned long channelID, char *driverFile); +static void *lookupSym(void *handle, char *name); +#endif + +static int openReader(int readerNum, int flags); + +typedef struct { + unsigned long channelID; + char *driverPath; + unsigned int driverLoaded; + /* for now, i'm only worry about the "bare essentials" */ + u_long (*open)(unsigned long channelID); + u_long (*close)(void); + u_long (*data)(struct SCARD_IO_HEADER junk, + unsigned char *cmdData, unsigned long cmdLen, + unsigned char *respData, unsigned long *respLen, + struct SCARD_IO_HEADER *moreJunk); + u_long (*power)(unsigned long command); + u_long (*getcapa)(unsigned long selector, unsigned char *buffer); + u_long (*setcapa)(unsigned long selector, unsigned char *buffer); + u_long (*cardpresent)(void); +} readerInfo; + +unsigned int numReaders; +readerInfo readers[MAX_READERS]; + unsigned char root_fid[] = {0x3f, 0x00}; +/* + if (config_path != NULL and driver_name != NULL) error; + if (config_path != NULL) use reader.conf there; + if (driver_path != NULL) use the specified driver; + if (config_path == NULL and driver_path == NULL) use /etc/reader.conf; + + Note that the config file is read only once, and drivers are only loaded once, + so config_path and driver_path are ignored on subsequent calls. +*/ + +int +sectok_xopen(int rn, int flags, char *config_path, char *driver_path, int *swp) +{ + int r = 0; + +#ifdef SCPERF + SetTime ("scopen() start"); +#endif /* SCPERF */ + + if (rn < 0 || rn >= MAX_READERS) { + r = STENOTTY; + goto out; + } + +#ifdef DL_READERS + if (driver_path) { + /* caller specified a particular driver path to use */ + if (config_path) { + /* but also specified a config file, which is an error. */ + r = STECNFFILES; + goto out; + } + if (!readers[rn].driverPath) { + /* need a driver */ + if (addReader(rn, (0x10000 + rn), driver_path) < 0) { + r = STEDRVR; + goto out; + } + } + } + + if (numReaders == 0) { + /* no drivers; read the config file */ + if (!config_path) + config_path = defaultConfigFilePath; + if (DBUpdateReaders(config_path, addReader) < 0) { + int i; + + if (config_path != defaultConfigFilePath) { + /* Something wrong with caller's config file path. */ + r = STEDRVR; + goto out; + } + /* This usually means there is no reader.conf. Supply defaults. */ + for (i = 0; i < N_DEFAULT_READERS; i++) + addReader(i, (0x10000 | i), defaultDriverPath); + } + } +#else + numReaders = N_DEFAULT_READERS; +#endif + + r = openReader(rn, flags); + + if (sectok_swOK(r) && !(flags & STONOWAIT)) { + /* Wait for card present */ + while (!sectok_cardpresent(rn)) { + errno = 0; + sleep(1); + if (errno == EINTR) { + r = STENOCARD; + break; + } + } + } + + out: +#ifdef SCPERF + SetTime ("scopen() end"); +#endif /* SCPERF */ + + if (swp) + *swp = r; + return (!sectok_swOK(r) ? -1 : rn); +} + +int sectok_open(int rn, int flags, int *swp) +{ + return sectok_xopen(rn, flags, NULL, NULL, swp); +} + +static int +openReader(int readerNum, int flags) +{ + readerInfo *reader; + +#ifdef DEBUG + fprintf(stderr, "openReader %d\n", readerNum); +#endif + + if (readerNum < 0 || readerNum >= MAX_READERS) + return STEDRVR; + reader = &readers[readerNum]; + + if (!reader->driverLoaded) { +#ifdef DL_READERS + void *libHandle; + + if (!reader->driverPath) + return STEDRVR; + libHandle = dlopen(reader->driverPath, RTLD_LAZY); + if (!libHandle) { +#ifdef DEBUG + fprintf(stderr, "%s: %s\n", reader->driverPath, dlerror()); +#endif + return STEDRVR; + } + reader->open = lookupSym(libHandle, "IO_Create_Channel"); + if (reader->open == NULL) + return STEDRVR; + + reader->close = lookupSym(libHandle, "IO_Close_Channel"); + if (reader->close == NULL) + return STEDRVR; + + reader->data = lookupSym(libHandle, "IFD_Transmit_to_ICC"); + if (reader->data == NULL) + return STEDRVR; + + reader->power = lookupSym(libHandle, "IFD_Power_ICC"); + if (reader->power == NULL) + return STEDRVR; + + reader->getcapa = lookupSym(libHandle, "IFD_Get_Capabilities"); + if (reader->getcapa == NULL) + return STEDRVR; + + reader->setcapa = lookupSym(libHandle, "IFD_Set_Capabilities"); + if (reader->setcapa == NULL) + return STEDRVR; + + reader->cardpresent = lookupSym(libHandle, "IFD_Is_ICC_Present"); +#else /* DL_READERS */ + reader->open = IO_Create_Channel; + reader->close = IO_Close_Channel; + reader->data = IFD_Transmit_to_ICC; + reader->power = IFD_Power_ICC; + reader->getcapa = IFD_Get_Capabilities; + reader->setcapa = IFD_Set_Capabilities; + reader->cardpresent = IFD_Is_ICC_Present; + reader->channelID = (0x10000 | readerNum); +#endif /* DL_READERS */ + + reader->driverLoaded = 1; + } + + /* send flags to the driver */ + flags ^= STONOWAIT; + reader->setcapa(SCTAG_OPEN_FLAGS, (u_char *)&flags); + /* if this returns an error, setcapa is not supported in this driver, + but that's OK. */ + + if (reader->open(reader->channelID)) + return STECOMM; + else + return STEOK; +} + +int +sectok_close(int fd) +{ + readerInfo *reader = &readers[fd]; + + if (fd < 0 || fd >= MAX_READERS) + return -1; + + reader = &readers[fd]; + + if (!reader->driverLoaded) + return -1; + + return (reader->close()) ? -1 : 0; +} + +int +sectok_cardpresent(int fd) +{ + readerInfo *reader = &readers[fd]; + unsigned long v; + + if (!reader->driverLoaded) + return 0; + + if (reader->cardpresent) + v = reader->cardpresent(); + else if (reader->getcapa(SCTAG_IFD_CARDPRESENT, (unsigned char *) &v)) + return 1; + + return (v == IFD_ICC_PRESENT || v == 0) ? 1 : 0; +} + +int +sectok_reset(int fd, int flags, unsigned char *atr, int *swp) +{ + readerInfo *reader = &readers[fd]; + int n = 0, r = STEOK; + +#ifdef SCPERF + SetTime ("scxreset() start"); +#endif /* SCPERF */ + + if (!reader->driverLoaded) { + r = STECLOSED; + goto out; + } + + if (!sectok_cardpresent(fd)) { + r = STENOCARD; + goto out; + } + + /* send flags to the driver */ + reader->setcapa(SCTAG_RESET_FLAGS, (u_char *)&flags); + /* if this returns an error, setcapa is not supported in this driver, + but that's OK. */ + + if (reader->power(IFD_RESET)) { +#ifdef DEBUG + fprintf(stderr, "power failed!\n"); +#endif + r = STESLAG; + goto out; + } + + if (atr && reader->getcapa(TAG_IFD_ATR, atr)) { +#ifdef DEBUG + fprintf(stderr, "reset failed!\n"); +#endif + r = STESLAG; + goto out; + } + + if (reader->getcapa(SCTAG_IFD_ATRLEN, (unsigned char *) &n) || n <= 0) { + /* can't get atr len, take a wild guess */ + if (atr) { + for (n = MAX_ATR_SIZE - 1; !atr[n]; n--) + ; + n--; + } else + n = MAX_ATR_SIZE; + } + + out: + if (swp) + *swp = r; + +#ifdef SCPERF + SetTime ("scxreset() end"); +#endif /* SCPERF */ + + return n; +} + +#ifdef DL_READERS +static int +addReader(int rn, unsigned long channelID, char *driverFile) +{ + readerInfo *reader; + + if (rn < 0 || rn >= MAX_READERS) + return -1; + + reader = &readers[rn]; + + if (reader->driverPath) + return -1; + + reader->channelID = channelID; + reader->driverPath = strdup(driverFile); + reader->driverLoaded = 0; + numReaders++; + return 0; +} + +static void * +lookupSym(void *handle, char *name) +{ +#ifdef __linux__ + return dlsym(handle, name); +#elif __sun + return dlsym(handle, name); +#else + char undername[32]; + + sprintf(undername, "_%s", name); + return dlsym(handle, undername); +#endif +} +#endif /* DL_READERS */ + +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) +{ + unsigned char cmd[6+255], rsp[255+2]; + unsigned long n; + int le; + readerInfo *reader = &readers[fd]; + struct SCARD_IO_HEADER garbage; + + if (reader->driverLoaded == 0) + return STECLOSED; + + cmd[0] = cla; + cmd[1] = ins; + cmd[2] = p1; + cmd[3] = p2; + + ilen &= 0xff; + le = (255 < olen) ? 255 : olen; + + if (ilen && ibuf) { + /* Send "in" data */ + cmd[4] = ilen; + memcpy(&cmd[5], ibuf, ilen); + ilen += 5; + if (le) + cmd[ilen++] = le; + n = obuf ? sizeof rsp : 2; + if (reader->data(garbage, cmd, ilen, rsp, &n, NULL) || n < 2) + return -1; + if (rsp[n-2] == 0x61 && olen && obuf) { + /* Response available; get it (driver should do this but some don't) */ + cmd[1] = 0xc0; + cmd[2] = cmd[3] = 0; + cmd[4] = rsp[n-1]; + n = sizeof rsp; + if (reader->data(garbage, cmd, 5, rsp, &n, NULL)) + return -1; + } + } else { + /* Get "out" data */ + cmd[4] = olen; + n = sizeof rsp; + if (reader->data(garbage, cmd, 5, rsp, &n, NULL)) + return -1; + } + + if (n >= 2) { + *swp = sectok_mksw(rsp[n-2], rsp[n-1]); + n -= 2; + } + + if (n && olen) + memcpy(obuf, rsp, (n < olen) ? n : olen); + + return n; +} + void sectok_fmt_fid(char *fname, int f0, int f1) { @@ -58,13 +458,12 @@ sectok_fmt_fid(char *fname, int f0, int f1) } int -sectok_selectfile(int fd, int cla, unsigned char *fid, int *r1p, int *r2p) +sectok_selectfile(int fd, int cla, unsigned char *fid, int *swp) { - int n, r1, r2; unsigned char obuf[256]; - n = scrw(fd, cla, 0xa4, 0, 0, 2, fid, sizeof obuf, obuf, &r1, &r2); - if (n < 0 || (r1 != 0x90 && r1 != 0x61)) + sectok_apdu(fd, cla, 0xa4, 0, 0, 2, fid, sizeof obuf, obuf, swp); + if (!sectok_swOK(*swp)) return -1; return 0; |