diff options
author | Jim Rees <rees@cvs.openbsd.org> | 2001-06-07 15:17:34 +0000 |
---|---|---|
committer | Jim Rees <rees@cvs.openbsd.org> | 2001-06-07 15:17:34 +0000 |
commit | e79bf55dc158d93c85332d7364cc081c5ed7f004 (patch) | |
tree | 0f156334f09069ae3055840248ec836e9d39d4cc /lib/libsectok/sc7816.c | |
parent | be748640a8100f6e89cc57bac4fe6358cb57b868 (diff) |
libsectok for secure tokens (smartcard, iButton, etc)
Diffstat (limited to 'lib/libsectok/sc7816.c')
-rw-r--r-- | lib/libsectok/sc7816.c | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/lib/libsectok/sc7816.c b/lib/libsectok/sc7816.c new file mode 100644 index 00000000000..8b0464f3667 --- /dev/null +++ b/lib/libsectok/sc7816.c @@ -0,0 +1,504 @@ +/* + * sc7816 library for use with pc/sc ifd drivers + * + * See copyright notice at end of file + * + * Jim Rees + * Mukesh Agrawal + * University of Michigan CITI, August 2000 + */ +static char *rcsid = "$Id: sc7816.c,v 1.1 2001/06/07 15:17:33 rees Exp $"; + +#include <sys/types.h> +#include <sys/time.h> +#include <stdio.h> +#include <string.h> +#include <dlfcn.h> + +#ifdef SCPERF +#define SCPERF_FIRST_APPEARANCE +#endif /* SCPERF */ + +#include "sectok.h" +#include "ifdhandler.h" + +#define MAX_READERS 32 + +#ifdef DL_READERS +static char defaultConfigFilePath[] = "/etc/reader.conf"; + +int DBUpdateReaders(char *readerconf, void (callback) (char *name, unsigned long channelId, char *driverFile)); + +/* the callback for DBUpdateReaders */ +void addReader(char *name, unsigned long channelID, char *driverFile); +void *lookupSym(void *handle, char *name); +#endif + +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; + +char *scerrtab[] = { + "ok", + "no such tty", + "out of memory", + "timeout", + "slag!", + "card type not supported", + "no card in reader", + "not implemented", + "error loading driver", + "communications error", + "reader not open", + "unknown error", +}; + +unsigned int numReaders; +readerInfo readers[MAX_READERS]; + +/* NI, 11/1/2000 : Now the body of scopen() is in scxopen() to allow + specifing a path to the config file or the driver. scopen() is + an entry function for scxopen() to maintain backward compatibility. */ +int +scopen(int ttyn, int flags, int *ep) +{ + return scxopen (ttyn, flags, ep, NULL, NULL); +} + +/* + NI: + + 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; +*/ +int +scxopen(int ttyn, int flags, int *ep, + char *config_path, char *driver_path) +{ + int i, r; +#ifdef DL_READERS + static char todos_driver_path[] = "/usr/local/pcsc/lib/libtodos_ag.so"; + char *configFilePath = defaultConfigFilePath; +#endif + +#ifdef SCPERF + SetTime ("scopen() start"); +#endif /* SCPERF */ + +#ifdef DL_READERS + if (config_path != NULL && driver_path != NULL) { + /* both config path and driver path are + specified. thus conflict. */ + return SCECNFFILES; + } + else if (config_path != NULL) { + /* config file specified */ + configFilePath = config_path; + } + else if (driver_path != NULL) { + /* driver path is specified */ + numReaders = 0; + + addReader(NULL, 0x10000, driver_path); + addReader(NULL, 0x10001, driver_path); + + goto open_readers; + } + + for (i = 0; i < numReaders; i++) { + if (readers[i].driverPath) { + free(readers[i].driverPath); + readers[i].driverPath = NULL; + } + } + numReaders = 0; + + if (DBUpdateReaders(configFilePath, addReader) < 0) { + /* This usually means there is no reader.conf. Supply a default. */ + addReader(NULL, 0x10000, todos_driver_path); + addReader(NULL, 0x10001, todos_driver_path); + } +#else + numReaders = 4; +#endif + + open_readers: + r = openReader(ttyn, flags); + if (ep) + *ep = r; + + if (!r && (flags & SCODSR)) { + /* Wait for card present */ + while (!sccardpresent(ttyn)) + sleep(1); + } + +#ifdef SCPERF + SetTime ("scopen() end"); +#endif /* SCPERF */ + + return r ? -1 : ttyn; +} + +int +openReader(int readerNum, int flags) +{ + void *libHandle; + readerInfo *reader; + +#ifdef DEBUG + fprintf(stderr, "openReader %d\n", readerNum); +#endif + + if (readerNum < 0 || readerNum >= numReaders) + return SCEDRVR; + reader = &readers[readerNum]; + + if (!reader->driverLoaded) { +#ifdef DL_READERS + if (!reader->driverPath) + return SCEDRVR; + libHandle = dlopen(reader->driverPath, RTLD_LAZY); + if (!libHandle) { +#ifdef DEBUG + fprintf(stderr, "%s: %s\n", reader->driverPath, dlerror()); +#endif + return SCEDRVR; + } + reader->open = lookupSym(libHandle, "IO_Create_Channel"); + if (reader->open == NULL) + return SCEDRVR; + + reader->close = lookupSym(libHandle, "IO_Close_Channel"); + if (reader->close == NULL) + return SCEDRVR; + + reader->data = lookupSym(libHandle, "IFD_Transmit_to_ICC"); + if (reader->data == NULL) + return SCEDRVR; + + reader->power = lookupSym(libHandle, "IFD_Power_ICC"); + if (reader->power == NULL) + return SCEDRVR; + + reader->getcapa = lookupSym(libHandle, "IFD_Get_Capabilities"); + if (reader->getcapa == NULL) + return SCEDRVR; + + reader->setcapa = lookupSym(libHandle, "IFD_Set_Capabilities"); + if (reader->setcapa == NULL) + return SCEDRVR; + + 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 */ + 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 SCECOMM; + else + return 0; +} + +int +scclose(int readerNum) +{ + readerInfo *reader = &readers[readerNum]; + + if (reader->driverLoaded == 0) + return -1; + + return (reader->close()) ? -1 : 0; +} + +int +sccardpresent(int ttyn) +{ + readerInfo *reader = &readers[ttyn]; + 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 +scxreset(int ttyn, int flags, unsigned char *atr, int *ep) +{ + readerInfo *reader = &readers[ttyn]; + int n = 0, r = SCEOK; + struct scparam param; + +#ifdef SCPERF + SetTime ("scxreset() start"); +#endif /* SCPERF */ + + if (reader->driverLoaded == 0) { + r = SCECLOSED; + goto out; + } + + if (!sccardpresent(ttyn)) { + r = SCENOCARD; + 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 = SCESLAG; + goto out; + } + + if (atr && reader->getcapa(TAG_IFD_ATR, atr)) { +#ifdef DEBUG + fprintf(stderr, "reset failed!\n"); +#endif + r = SCESLAG; + 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; + } + + if (flags & SCRV) + parse_atr(-1, flags, atr, n, ¶m); + + out: + if (ep) + *ep = r; + +#ifdef SCPERF + SetTime ("scxreset() end"); +#endif /* SCPERF */ + + return n; +} + +int +screset(int ttyn, unsigned char *atr, int *ep) +{ + return scxreset(ttyn, 0, atr, ep); +} + +int +scrw(int ttyn, int cla, int ins, int p1, int p2, int ilen, unsigned char *ibuf, int olen, unsigned char *obuf, int *sw1p, int *sw2p) +{ + unsigned char cmd[6+255], rsp[255+2]; + unsigned long n; + int le; + readerInfo *reader = &readers[ttyn]; + struct SCARD_IO_HEADER garbage; + + if (reader->driverLoaded == 0) + return SCECLOSED; + + 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) { + *sw1p = rsp[n-2]; + *sw2p = rsp[n-1]; + n -= 2; + } + + if (n && olen) + memcpy(obuf, rsp, (n < olen) ? n : olen); + + return n; +} + +int +scwrite(int ttyn, int cla, int ins, int p1, int p2, int p3, unsigned char *buf, int *sw1p, int *sw2p) +{ + int rv; +#ifdef SCPERF + char *scperf_buf; + + scperf_buf = malloc (64); + + sprintf (scperf_buf, "scwrite (ins %02x, p3 %02x) start", ins, p3); + SetTime(scperf_buf); + + /* Ihis does not free scperf. It looks like memory leak ... + and it is, but it is actually the right behavior. + print_time() will print the messages later, so the buffer + must be there. */ +#endif /* SCPERF */ + rv = scrw(ttyn, cla, ins, p1, p2, p3, buf, 0, NULL, sw1p, sw2p); + +#ifdef SCPERF + SetTime("scwrite() end"); +#endif /* SCPERF */ + return (rv >= 0) ? p3 : -1; +} + +int +scread(int ttyn, int cla, int ins, int p1, int p2, int p3, unsigned char *buf, int *sw1p, int *sw2p) +{ + int rv; +#ifdef SCPERF + char *scperf_buf; + + scperf_buf = malloc (64); + + sprintf (scperf_buf, "scread (ins %02x, p3 %02x) start", ins, p3); + SetTime(scperf_buf); + + /* Ihis does not free scperf. It looks like memory leak ... + and it is, but it is actually the right behavior. + print_time() will print the messages later, so the buffer + must be there. */ +#endif /* SCPERF */ + rv = scrw(ttyn, cla, ins, p1, p2, 0, NULL, p3, buf, sw1p, sw2p); + +#ifdef SCPERF + SetTime("scread() end"); +#endif /* SCPERF */ + return rv; +} + +#ifdef DL_READERS +void +addReader(char *name, unsigned long channelID, char *driverFile) +{ + readerInfo *reader; + + if (numReaders >= MAX_READERS) + return; + + reader = &readers[numReaders++]; + + reader->channelID = channelID; + reader->driverPath = strdup(driverFile); + reader->driverLoaded = 0; +} + +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 */ + +/* +copyright 2000 +the regents of the university of michigan +all rights reserved + +permission is granted to use, copy, create derivative works +and redistribute this software and such derivative works +for any purpose, so long as the name of the university of +michigan is not used in any advertising or publicity +pertaining to the use or distribution of this software +without specific, written prior authorization. if the +above copyright notice or any other identification of the +university of michigan is included in any copy of any +portion of this software, then the disclaimer below must +also be included. + +this software is provided as is, without representation +from the university of michigan as to its fitness for any +purpose, and without warranty by the university of +michigan of any kind, either express or implied, including +without limitation the implied warranties of +merchantability and fitness for a particular purpose. the +regents of the university of michigan shall not be liable +for any damages, including special, indirect, incidental, or +consequential damages, with respect to any claim arising +out of or in connection with the use of the software, even +if it has been or is hereafter advised of the possibility of +such damages. +*/ |