371 lines
8.5 KiB
C
371 lines
8.5 KiB
C
/*
|
|
* reader-openct.c: backend for OpenCT
|
|
*
|
|
* Copyright (C) 2003 Olaf Kirch <okir@suse.de>
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/un.h>
|
|
#include <unistd.h>
|
|
|
|
#include <openct/openct.h>
|
|
#include <openct/logging.h>
|
|
#include <openct/error.h>
|
|
|
|
#include "opensc.h"
|
|
#include "internal.h"
|
|
#include "log.h"
|
|
|
|
/* If you set PREALLOCATE to a non-zero value, this backend
|
|
* will allocate that many reader slots. This will allow hot-
|
|
* plugging devices (such as USB tokens) while OpenSC is running.
|
|
*
|
|
* To disable this, set PREALLOCATE to 0.
|
|
*
|
|
* This will most likely become a config file option soon.
|
|
*/
|
|
#define PREALLOCATE 5
|
|
|
|
/* function declarations */
|
|
static int openct_reader_init(struct sc_context *ctx, void **priv_data);
|
|
static int openct_add_reader(struct sc_context *ctx, unsigned int num, ct_info_t *info);
|
|
static int openct_reader_finish(struct sc_context *ctx, void *priv_data);
|
|
static int openct_reader_release(struct sc_reader *reader);
|
|
static int openct_reader_detect_card_presence(struct sc_reader *reader,
|
|
struct sc_slot_info *slot);
|
|
static int openct_reader_connect(struct sc_reader *reader,
|
|
struct sc_slot_info *slot);
|
|
static int openct_reader_disconnect(struct sc_reader *reader,
|
|
struct sc_slot_info *slot, int action);
|
|
static int openct_reader_transmit(struct sc_reader *reader,
|
|
struct sc_slot_info *slot,
|
|
const u8 *sendbuf, size_t sendsize,
|
|
u8 *recvbuf, size_t *recvsize, int control);
|
|
static int openct_reader_lock(struct sc_reader *reader,
|
|
struct sc_slot_info *slot);
|
|
static int openct_reader_unlock(struct sc_reader *reader,
|
|
struct sc_slot_info *slot);
|
|
static int openct_error(struct sc_reader *, int);
|
|
|
|
/* the operations struct, already initialized */
|
|
static struct sc_reader_operations openct_reader_operations = {
|
|
.init = openct_reader_init,
|
|
.finish = openct_reader_finish,
|
|
.release = openct_reader_release,
|
|
.detect_card_presence = openct_reader_detect_card_presence,
|
|
.connect = openct_reader_connect,
|
|
.disconnect = openct_reader_disconnect,
|
|
.transmit = openct_reader_transmit,
|
|
.lock = openct_reader_lock,
|
|
.unlock = openct_reader_unlock,
|
|
};
|
|
|
|
/* also, the driver struct */
|
|
static struct sc_reader_driver openct_reader_driver = {
|
|
.name = "OpenCT Reader",
|
|
.short_name = "openct",
|
|
.ops = &openct_reader_operations
|
|
};
|
|
|
|
/* return our structure */
|
|
const struct sc_reader_driver *sc_get_openct_driver() {
|
|
return &openct_reader_driver;
|
|
};
|
|
|
|
/* private data structures */
|
|
struct driver_data {
|
|
ct_handle * h;
|
|
unsigned int num;
|
|
ct_info_t info;
|
|
};
|
|
|
|
struct slot_data {
|
|
ct_lock_handle excl_lock;
|
|
ct_lock_handle shared_lock;
|
|
};
|
|
|
|
/*
|
|
* Initialize readers
|
|
*
|
|
* Called during sc_establish_context(), when the driver
|
|
* is loaded
|
|
*/
|
|
static int
|
|
openct_reader_init(struct sc_context *ctx, void **priv_data)
|
|
{
|
|
unsigned int i;
|
|
|
|
SC_FUNC_CALLED(ctx, 1);
|
|
for (i = 0; i < OPENCT_MAX_READERS; i++) {
|
|
ct_info_t info;
|
|
|
|
if (ct_reader_info(i, &info) >= 0) {
|
|
openct_add_reader(ctx, i, &info);
|
|
} else if (i < PREALLOCATE) {
|
|
openct_add_reader(ctx, i, NULL);
|
|
}
|
|
}
|
|
|
|
return SC_NO_ERROR;
|
|
}
|
|
|
|
static int
|
|
openct_add_reader(struct sc_context *ctx, unsigned int num, ct_info_t *info)
|
|
{
|
|
sc_reader_t *reader;
|
|
struct driver_data *data;
|
|
int rc, i;
|
|
|
|
if (!(reader = calloc(1, sizeof(*reader)))
|
|
|| !(data = (calloc(1, sizeof(*data))))) {
|
|
if (reader)
|
|
free(reader);
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (info) {
|
|
data->info = *info;
|
|
} else {
|
|
strcpy(data->info.ct_name, "OpenCT reader (detached)");
|
|
data->info.ct_slots = 1;
|
|
}
|
|
data->num = num;
|
|
|
|
reader->driver = &openct_reader_driver;
|
|
reader->ops = &openct_reader_operations;
|
|
reader->drv_data = data;
|
|
reader->name = strdup(data->info.ct_name);
|
|
reader->slot_count = data->info.ct_slots;
|
|
|
|
if ((rc = _sc_add_reader(ctx, reader)) < 0) {
|
|
free(data);
|
|
free(reader->name);
|
|
free(reader);
|
|
return rc;
|
|
}
|
|
|
|
for (i = 0; i < SC_MAX_SLOTS; i++) {
|
|
reader->slot[i].drv_data = calloc(1, sizeof(struct slot_data));
|
|
reader->slot[i].id = i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Called when the driver is being unloaded. finish() has to
|
|
* deallocate the private data and any resources.
|
|
*/
|
|
int
|
|
openct_reader_finish(struct sc_context *ctx, void *priv_data)
|
|
{
|
|
SC_FUNC_CALLED(ctx, 1);
|
|
return SC_NO_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Called when releasing a reader. release() has to
|
|
* deallocate the private data. Other fields will be
|
|
* freed by OpenSC.
|
|
*/
|
|
int
|
|
openct_reader_release(struct sc_reader *reader)
|
|
{
|
|
struct driver_data *data = (struct driver_data *) reader->drv_data;
|
|
|
|
SC_FUNC_CALLED(reader->ctx, 1);
|
|
if (data) {
|
|
if (data->h)
|
|
ct_reader_disconnect(data->h);
|
|
memset(data, 0, sizeof(*data));
|
|
reader->drv_data = NULL;
|
|
free(data);
|
|
}
|
|
|
|
return SC_NO_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Check whether a card was added/removed
|
|
*/
|
|
int
|
|
openct_reader_detect_card_presence(struct sc_reader *reader,
|
|
struct sc_slot_info *slot)
|
|
{
|
|
struct driver_data *data = (struct driver_data *) reader->drv_data;
|
|
int rc, status;
|
|
|
|
SC_FUNC_CALLED(reader->ctx, 1);
|
|
|
|
slot->flags = 0;
|
|
if (!data->h && !(data->h = ct_reader_connect(data->num)))
|
|
return 0;
|
|
|
|
if ((rc = ct_card_status(data->h, slot->id, &status)) < 0)
|
|
return SC_ERROR_TRANSMIT_FAILED;
|
|
|
|
if (status & IFD_CARD_PRESENT) {
|
|
slot->flags = SC_SLOT_CARD_PRESENT;
|
|
if (status & IFD_CARD_STATUS_CHANGED)
|
|
slot->flags = SC_SLOT_CARD_PRESENT;
|
|
}
|
|
return slot->flags;
|
|
}
|
|
|
|
static int
|
|
openct_reader_connect(struct sc_reader *reader,
|
|
struct sc_slot_info *slot)
|
|
{
|
|
struct driver_data *data = (struct driver_data *) reader->drv_data;
|
|
int rc;
|
|
|
|
SC_FUNC_CALLED(reader->ctx, 1);
|
|
|
|
if (data->h)
|
|
ct_reader_disconnect(data->h);
|
|
|
|
if (!(data->h = ct_reader_connect(data->num))) {
|
|
error(reader->ctx, "ct_reader_connect socket failed\n");
|
|
return SC_ERROR_CARD_NOT_PRESENT;
|
|
}
|
|
|
|
rc = ct_card_request(data->h, slot->id, 0, NULL,
|
|
slot->atr, sizeof(slot->atr));
|
|
if (rc < 0) {
|
|
error(reader->ctx,
|
|
"openct_reader_connect read failed: %s\n",
|
|
ct_strerror(rc));
|
|
return SC_ERROR_CARD_NOT_PRESENT;
|
|
}
|
|
|
|
if (rc == 0) {
|
|
error(reader->ctx, "openct_reader_connect recved no data\n");
|
|
return SC_ERROR_READER;
|
|
}
|
|
|
|
slot->atr_len = rc;
|
|
return SC_NO_ERROR;
|
|
}
|
|
|
|
static int
|
|
openct_reader_reconnect(struct sc_reader *reader,
|
|
struct sc_slot_info *slot)
|
|
{
|
|
struct driver_data *data = (struct driver_data *) reader->drv_data;
|
|
int rc;
|
|
|
|
if (data->h != NULL)
|
|
return 0;
|
|
|
|
if ((rc = openct_reader_connect(reader, slot)) < 0)
|
|
return SC_ERROR_READER_DETACHED;
|
|
return SC_ERROR_READER_REATTACHED;
|
|
}
|
|
|
|
int
|
|
openct_reader_disconnect(struct sc_reader *reader,
|
|
struct sc_slot_info *slot, int action)
|
|
{
|
|
struct driver_data *data = (struct driver_data *) reader->drv_data;
|
|
|
|
SC_FUNC_CALLED(reader->ctx, 1);
|
|
if (data->h)
|
|
ct_reader_disconnect(data->h);
|
|
data->h = NULL;
|
|
return SC_NO_ERROR;
|
|
}
|
|
|
|
int
|
|
openct_reader_transmit(struct sc_reader *reader,
|
|
struct sc_slot_info *slot,
|
|
const u8 *sendbuf, size_t sendsize,
|
|
u8 *recvbuf, size_t *recvsize, int control)
|
|
{
|
|
struct driver_data *data = (struct driver_data *) reader->drv_data;
|
|
int rc;
|
|
|
|
/* Hotplug check */
|
|
if ((rc = openct_reader_reconnect(reader, slot)) < 0)
|
|
return rc;
|
|
|
|
rc = ct_card_transact(data->h, slot->id,
|
|
sendbuf, sendsize,
|
|
recvbuf, *recvsize);
|
|
|
|
if (rc == IFD_ERROR_NOT_CONNECTED) {
|
|
ct_reader_disconnect(data->h);
|
|
data->h = NULL;
|
|
return SC_ERROR_READER_DETACHED;
|
|
}
|
|
|
|
if (rc >= 0)
|
|
*recvsize = rc;
|
|
|
|
return openct_error(reader, rc);
|
|
}
|
|
|
|
int
|
|
openct_reader_lock(struct sc_reader *reader,
|
|
struct sc_slot_info *slot)
|
|
{
|
|
struct driver_data *data = (struct driver_data *) reader->drv_data;
|
|
struct slot_data *slot_data = (struct slot_data *) slot->drv_data;
|
|
int rc, retry = 1;
|
|
|
|
SC_FUNC_CALLED(reader->ctx, 1);
|
|
|
|
/* Hotplug check */
|
|
if ((rc = openct_reader_reconnect(reader, slot)) < 0)
|
|
return rc;
|
|
|
|
rc = ct_card_lock(data->h, slot->id,
|
|
IFD_LOCK_EXCLUSIVE,
|
|
&slot_data->excl_lock);
|
|
|
|
if (rc == IFD_ERROR_NOT_CONNECTED) {
|
|
ct_reader_disconnect(data->h);
|
|
data->h = NULL;
|
|
return SC_ERROR_READER_DETACHED;
|
|
}
|
|
|
|
return openct_error(reader, rc);
|
|
}
|
|
|
|
int
|
|
openct_reader_unlock(struct sc_reader *reader,
|
|
struct sc_slot_info *slot)
|
|
{
|
|
struct driver_data *data = (struct driver_data *) reader->drv_data;
|
|
struct slot_data *slot_data = (struct slot_data *) slot->drv_data;
|
|
int rc;
|
|
|
|
SC_FUNC_CALLED(reader->ctx, 1);
|
|
|
|
rc = ct_card_unlock(data->h, slot->id, slot_data->excl_lock);
|
|
|
|
/* We couldn't care less */
|
|
if (rc == IFD_ERROR_NOT_CONNECTED)
|
|
return 0;
|
|
|
|
return openct_error(reader, rc);
|
|
}
|
|
|
|
/*
|
|
* Handle an error code returned by OpenCT
|
|
*/
|
|
int
|
|
openct_error(struct sc_reader *reader, int code)
|
|
{
|
|
if (code >= 0)
|
|
return code;
|
|
|
|
/* Fixme: translate error code */
|
|
return SC_ERROR_READER;
|
|
}
|