예제 #1
0
/*
 * Load a module - this will load the shared object, call
 * C_Initialize, and get the list of function pointers
 */
void *
C_LoadModule(const char *mspec, CK_FUNCTION_LIST_PTR_PTR funcs)
{
	sc_pkcs11_module_t *mod;
	CK_RV rv, (*c_get_function_list)(CK_FUNCTION_LIST_PTR_PTR);
	mod = calloc(1, sizeof(*mod));
	mod->_magic = MAGIC;

	if (mspec == NULL) {
		free(mod);
		return NULL;
	}
	mod->handle = sc_dlopen(mspec);
	if (mod->handle == NULL) {
		fprintf(stderr, "sc_dlopen failed: %s\n", sc_dlerror());
		goto failed;
	}

	/* Get the list of function pointers */
	c_get_function_list = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR))
				sc_dlsym(mod->handle, "C_GetFunctionList");
	if (!c_get_function_list)
		goto failed;
	rv = c_get_function_list(funcs);
	if (rv == CKR_OK)
		return (void *) mod;
	else
		fprintf(stderr, "C_GetFunctionList failed %lx", rv);
failed:
	C_UnloadModule((void *) mod);
	free(mod);
	return NULL;
}
예제 #2
0
파일: ctx.c 프로젝트: AktivCo/OpenSC
/**
 * load card/reader driver modules
 * Every module should contain a function " void * sc_module_init(char *) "
 * that returns a pointer to the function _sc_get_xxxx_driver()
 * used to initialize static modules
 * Also, an exported "char *sc_module_version" variable should exist in module
 */
static void *load_dynamic_driver(sc_context_t *ctx, void **dll, const char *name)
{
	const char *version, *libname;
	void *handle;
	void *(*modinit)(const char *) = NULL;
	void *(**tmodi)(const char *) = &modinit;
	const char *(*modversion)(void) = NULL;
	const char *(**tmodv)(void) = &modversion;

	if (dll == NULL) {
		sc_log(ctx, "No dll parameter specified");
		return NULL;
	}
	if (name == NULL) { /* should not occur, but... */
		sc_log(ctx, "No module specified");
		return NULL;
	}
	libname = find_library(ctx, name);
	if (libname == NULL)
		return NULL;
	handle = sc_dlopen(libname);
	if (handle == NULL) {
		sc_log(ctx, "Module %s: cannot load %s library: %s", name, libname, sc_dlerror());
		return NULL;
	}

	/* verify correctness of module */
	*(void **)tmodi = sc_dlsym(handle, "sc_module_init");
	*(void **)tmodv = sc_dlsym(handle, "sc_driver_version");
	if (modinit == NULL || modversion == NULL) {
		sc_log(ctx, "dynamic library '%s' is not a OpenSC module",libname);
		sc_dlclose(handle);
		return NULL;
	}
	/* verify module version */
	version = modversion();
	/* XXX: We really need to have ABI version for each interface */
	if (version == NULL || strncmp(version, PACKAGE_VERSION, strlen(PACKAGE_VERSION)) != 0) {
		sc_log(ctx, "dynamic library '%s': invalid module version", libname);
		sc_dlclose(handle);
		return NULL;
	}

	*dll = handle;
	sc_log(ctx, "successfully loaded card driver '%s'", name);
	return modinit(name);
}
예제 #3
0
static int ctapi_load_module(sc_context_t *ctx,
			     struct ctapi_global_private_data *gpriv,
			     scconf_block *conf)
{
	const char *val;
	struct ctapi_functions funcs;
	struct ctapi_module *mod;
	const scconf_list *list;
	void *dlh;
	int r, i, NumUnits;
	u8 cmd[5], rbuf[256], sad, dad;
	unsigned short lr;

	
	
	list = scconf_find_list(conf, "ports");
	if (list == NULL) {
		sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "No ports configured.\n");
		return -1;
	}

	val = conf->name->data;
	dlh = sc_dlopen(val);
	if (!dlh) {
		sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Unable to open shared library '%s': %s\n", val, sc_dlerror());
		return -1;
	}

	funcs.CT_init = (CT_INIT_TYPE *) sc_dlsym(dlh, "CT_init");
	if (!funcs.CT_init)
		goto symerr;
	funcs.CT_close = (CT_CLOSE_TYPE *) sc_dlsym(dlh, "CT_close");
	if (!funcs.CT_close)
		goto symerr;
	funcs.CT_data = (CT_DATA_TYPE *) sc_dlsym(dlh, "CT_data");
	if (!funcs.CT_data)
		goto symerr;

	mod = add_module(gpriv, val, dlh);
	for (; list != NULL; list = list->next) {
		int port;
		char namebuf[128];
		char rv;
		sc_reader_t *reader;
		struct ctapi_private_data *priv;
		
		if (sscanf(list->data, "%d", &port) != 1) {
			sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Port '%s' is not a number.\n", list->data);
			continue;
		}
		rv = funcs.CT_init((unsigned short)mod->ctn_count, (unsigned short)port);
		if (rv) {
			sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "CT_init() failed with %d\n", rv);
			continue;
		}
		
		reader = calloc(1, sizeof(sc_reader_t));
		priv = calloc(1, sizeof(struct ctapi_private_data));
		if (!priv || !reader) {
			free(reader);
			free(priv);
			return SC_ERROR_OUT_OF_MEMORY;
		}
		reader->drv_data = priv;
		reader->ops = &ctapi_ops;
		reader->driver = &ctapi_drv;
		snprintf(namebuf, sizeof(namebuf), "CT-API %s, port %d", mod->name, port);
		reader->name = strdup(namebuf);
		priv->funcs = funcs;
		priv->ctn = mod->ctn_count;
		r = _sc_add_reader(ctx, reader);
		if (r) {
			funcs.CT_close((unsigned short)mod->ctn_count);
			free(priv);
			free(reader->name);
			free(reader);
			break;
		}
		
		/* Detect functional units of the reader according to CT-BCS spec version 1.0 
		(14.04.2004, http://www.teletrust.de/down/mct1-0_t4.zip) */	
		cmd[0] = CTBCS_CLA;
		cmd[1] = CTBCS_INS_STATUS;
		cmd[2] = CTBCS_P1_CT_KERNEL;
		cmd[3] = CTBCS_P2_STATUS_TFU;
		cmd[4] = 0x00;
		dad = 1;
		sad = 2;
		lr = 256;
		
		rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf);
		if (rv || (lr < 4) || (rbuf[lr-2] != 0x90)) {
			sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error getting status of terminal: %d, using defaults\n", rv);
		}
		if (rbuf[0] != CTBCS_P2_STATUS_TFU) {
			/* Number of slots might also detected by using CTBCS_P2_STATUS_ICC.
			   If you think that's important please do it... ;) */
			sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Invalid data object returnd on CTBCS_P2_STATUS_TFU: 0x%x\n", rbuf[0]);
		}
		NumUnits = rbuf[1];
		if (NumUnits + 4 > lr) {
			sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Invalid data returnd: %d functional units, size %d\n", NumUnits, rv);
		}
		priv->ctapi_functional_units = 0;
		for(i = 0; i < NumUnits; i++) {
			switch(rbuf[i+2]) {
				case CTBCS_P1_INTERFACE1:
				case CTBCS_P1_INTERFACE2:
				case CTBCS_P1_INTERFACE3:
				case CTBCS_P1_INTERFACE4:
				case CTBCS_P1_INTERFACE5:
				case CTBCS_P1_INTERFACE6:
				case CTBCS_P1_INTERFACE7:
				case CTBCS_P1_INTERFACE8:
				case CTBCS_P1_INTERFACE9:
				case CTBCS_P1_INTERFACE10:
				case CTBCS_P1_INTERFACE11:
				case CTBCS_P1_INTERFACE12:
				case CTBCS_P1_INTERFACE13:
				case CTBCS_P1_INTERFACE14:
				/* Maybe a weak point here if multiple interfaces are present and not returned
				   in the "canonical" order. This is not forbidden by the specs, but why should
				   anyone want to do that? */
					sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Found slot id 0x%x\n", rbuf[i+2]);
					break;

				case CTBCS_P1_DISPLAY:
					priv->ctapi_functional_units |= CTAPI_FU_DISPLAY;
					sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Display detected\n");
					break;

				case CTBCS_P1_KEYPAD:
					priv->ctapi_functional_units |= CTAPI_FU_KEYBOARD;
					sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Keypad detected\n");
					break;

				case CTBCS_P1_PRINTER:
					priv->ctapi_functional_units |= CTAPI_FU_PRINTER;
					sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Printer detected\n");
					break;

				case CTBCS_P1_FINGERPRINT:
				case CTBCS_P1_VOICEPRINT:
				case CTBCS_P1_DSV:
				case CTBCS_P1_FACE_RECOGNITION:
				case CTBCS_P1_IRISSCAN:
					priv->ctapi_functional_units |= CTAPI_FU_BIOMETRIC;
					sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Biometric sensor detected\n");
					break;

				default:
					sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Unknown functional unit 0x%x\n", rbuf[i+2]);
			}
		}
		/* CT-BCS does not define Keyboard/Display for each slot, so I assume
		those additional units can be used for each slot */
		if (priv->ctapi_functional_units) {
			if (priv->ctapi_functional_units & CTAPI_FU_KEYBOARD)
				reader->capabilities |= SC_READER_CAP_PIN_PAD;
			if (priv->ctapi_functional_units & CTAPI_FU_DISPLAY)
				reader->capabilities |= SC_READER_CAP_DISPLAY;
		}
		
		ctapi_reset(reader);
		refresh_attributes(reader);
		mod->ctn_count++;
	}
	return 0;
symerr:
	sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Unable to resolve CT-API symbols.\n");
	sc_dlclose(dlh);
	return -1;
}
예제 #4
0
파일: card.c 프로젝트: AktivCo/OpenSC
static int
sc_card_sm_load(struct sc_card *card, const char *module_path, const char *in_module)
{
	struct sc_context *ctx = NULL;
	int rv = SC_ERROR_INTERNAL;
	char *module = NULL;
#ifdef _WIN32
	char temp_path[PATH_MAX];
	size_t temp_len;
	const char path_delim = '\\';
	char expanded_val[PATH_MAX];
	DWORD expanded_len;
#else
	const char path_delim = '/';
#endif

	if (card == NULL) {
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	ctx = card->ctx;
	LOG_FUNC_CALLED(ctx);
	if (!in_module)
		return sc_card_sm_unload(card);

#ifdef _WIN32
	if (!module_path || strlen(module_path) == 0)   {
		temp_len = PATH_MAX-1;
		rv = sc_ctx_win32_get_config_value(NULL, "SmDir", "Software\\OpenSC Project\\OpenSC",
				temp_path, &temp_len);
		if (rv == SC_SUCCESS) {
			temp_path[temp_len] = '\0';
			module_path = temp_path;
		}
	}
	expanded_len = PATH_MAX;
	expanded_len = ExpandEnvironmentStringsA(module_path, expanded_val, expanded_len);
	if (0 < expanded_len && expanded_len < sizeof expanded_val)
		module_path = expanded_val;
#endif
	sc_log(ctx, "SM module '%s' located in '%s'", in_module, module_path);
	if (module_path && strlen(module_path) > 0)   {
		int sz = strlen(in_module) + strlen(module_path) + 3;
		module = malloc(sz);
		if (module)
			snprintf(module, sz, "%s%c%s", module_path, path_delim, in_module);
	}
	else   {
		module = strdup(in_module);
	}

	if (!module)
		return SC_ERROR_OUT_OF_MEMORY;

	sc_log(ctx, "try to load SM module '%s'", module);
	do  {
		struct sm_module_operations *mod_ops = &card->sm_ctx.module.ops;
		void *mod_handle;

		card->sm_ctx.module.handle = sc_dlopen(module);
		if (!card->sm_ctx.module.handle)   {
			sc_log(ctx, "cannot open dynamic library '%s': %s", module, sc_dlerror());
			break;
		}
		mod_handle = card->sm_ctx.module.handle;

		mod_ops->initialize = sc_dlsym(mod_handle, "initialize");
		if (!mod_ops->initialize)   {
			sc_log(ctx, "SM handler 'initialize' not exported: %s", sc_dlerror());
			break;
		}

		mod_ops->get_apdus  = sc_dlsym(mod_handle, "get_apdus");
		if (!mod_ops->get_apdus)   {
			sc_log(ctx, "SM handler 'get_apdus' not exported: %s", sc_dlerror());
			break;
		}

		mod_ops->finalize  = sc_dlsym(mod_handle, "finalize");
		if (!mod_ops->finalize)
			sc_log(ctx, "SM handler 'finalize' not exported -- ignored");

		mod_ops->module_init  = sc_dlsym(mod_handle, "module_init");
		if (!mod_ops->module_init)
			sc_log(ctx, "SM handler 'module_init' not exported -- ignored");

		mod_ops->module_cleanup  = sc_dlsym(mod_handle, "module_cleanup");
		if (!mod_ops->module_cleanup)
			sc_log(ctx, "SM handler 'module_cleanup' not exported -- ignored");

		mod_ops->test  = sc_dlsym(mod_handle, "test");
		if (mod_ops->test)
			sc_log(ctx, "SM handler 'test' not exported -- ignored");

		rv = 0;
		break;
	} while(0);

	if (rv)
		sc_card_sm_unload(card);

	card->sm_ctx.sm_mode = SM_MODE_ACL;
	if (module)
		free(module);

	SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, rv);
}
예제 #5
0
/**
 * find library module for provided driver in configuration file
 * if not found assume library name equals to module name
 */
static const char *find_library(sc_context_t *ctx, const char *name)
{
	int          i;
	const char   *libname = NULL;
	scconf_block **blocks, *blk;

	for (i = 0; ctx->conf_blocks[i]; i++) {
		blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "card_driver", name);
		if (!blocks)
			continue;
		blk = blocks[0];
		free(blocks);
		if (blk == NULL)
			continue;
		libname = scconf_get_str(blk, "module", name);
#ifdef _WIN32
		if (libname && libname[0] != '\\' ) {
#else
		if (libname && libname[0] != '/' ) {
#endif
			sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "warning: relative path to driver '%s' used",
				 libname);
		}
		break;
	}

	return libname;
}

/**
 * load card/reader driver modules
 * Every module should contain a function " void * sc_module_init(char *) "
 * that returns a pointer to the function _sc_get_xxxx_driver()
 * used to initialize static modules
 * Also, an exported "char *sc_module_version" variable should exist in module
 */
static void *load_dynamic_driver(sc_context_t *ctx, void **dll, const char *name)
{
	const char *version, *libname;
	void *handle;
	void *(*modinit)(const char *) = NULL;
	void *(**tmodi)(const char *) = &modinit;
	const char *(*modversion)(void) = NULL;
	const char *(**tmodv)(void) = &modversion;

	if (name == NULL) { /* should not occurr, but... */
		sc_debug(ctx, SC_LOG_DEBUG_NORMAL,"No module specified",name);
		return NULL;
	}
	libname = find_library(ctx, name);
	if (libname == NULL)
		return NULL;
	handle = sc_dlopen(libname);
	if (handle == NULL) {
		sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Module %s: cannot load %s library: %s", name, libname, sc_dlerror());
		return NULL;
	}

	/* verify correctness of module */
	*(void **)tmodi = sc_dlsym(handle, "sc_module_init");
	*(void **)tmodv = sc_dlsym(handle, "sc_driver_version");
	if (modinit == NULL || modversion == NULL) {
		sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "dynamic library '%s' is not a OpenSC module",libname);
		sc_dlclose(handle);
		return NULL;
	}
	/* verify module version */
	version = modversion();
	/* XXX: We really need to have ABI version for each interface */
	if (version == NULL || strncmp(version, PACKAGE_VERSION, strlen(PACKAGE_VERSION)) != 0) {
		sc_debug(ctx, SC_LOG_DEBUG_NORMAL,"dynamic library '%s': invalid module version",libname);
		sc_dlclose(handle);
		return NULL;
	}
	*dll = handle;
	sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "successfully loaded card driver '%s'", name);
	return modinit(name);
}

static int load_card_driver_options(sc_context_t *ctx,
				    struct sc_card_driver *driver)
{
	scconf_block **blocks, *blk;
	int i;

	for (i = 0; ctx->conf_blocks[i]; i++) {
		blocks = scconf_find_blocks(ctx->conf,
					ctx->conf_blocks[i],
					"card_driver", driver->short_name);
		if (!blocks)
			continue;
		blk = blocks[0];
		free(blocks);

		if (blk == NULL)
			continue;

		/* no options at the moment */
	}
	return SC_SUCCESS;
}
예제 #6
0
static int parse_emu_block(sc_pkcs15_card_t *p15card, scconf_block *conf)
{
	sc_card_t	*card = p15card->card;
	sc_context_t	*ctx = card->ctx;
	sc_pkcs15emu_opt_t opts;
	void *handle = NULL;
	int		(*init_func)(sc_pkcs15_card_t *);
	int		(*init_func_ex)(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *);
	int		r, force = 0;
	const char	*driver, *module_name;

	driver = conf->name->data;

	init_func    = NULL;
	init_func_ex = NULL;

	memset(&opts, 0, sizeof(opts));
	opts.blk     = conf;
	if (force != 0)
		opts.flags   = SC_PKCS15EMU_FLAGS_NO_CHECK;

	module_name = scconf_get_str(conf, "module", builtin_name);
	if (!strcmp(module_name, "builtin")) {
		int	i;

		/* This function is built into libopensc itself.
		 * Look it up in the table of emulators */
		module_name = driver;
		for (i = 0; builtin_emulators[i].name; i++) {
			if (!strcmp(builtin_emulators[i].name, module_name)) {
				init_func_ex = builtin_emulators[i].handler;
				break;
			}
		}
	} else {
		const char *(*get_version)(void);
		const char *name = NULL;
		void	*address;

		sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Loading %s\n", module_name);
		
		/* try to open dynamic library */
		handle = sc_dlopen(module_name);
		if (!handle) {
			sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "unable to open dynamic library '%s': %s\n",
			         module_name, sc_dlerror());
			return SC_ERROR_INTERNAL;
		}
		/* try to get version of the driver/api */
		get_version =  (const char *(*)(void)) sc_dlsym(handle, "sc_driver_version");
		if (!get_version || strcmp(get_version(), "0.9.3") < 0) {
			/* no sc_driver_version function => assume old style
			 * init function (note: this should later give an error
			 */
			/* get the init function name */
			name = scconf_get_str(conf, "function", func_name);

			address = sc_dlsym(handle, name);
			if (address)
				init_func = (int (*)(sc_pkcs15_card_t *)) address;
		} else {
			name = scconf_get_str(conf, "function", exfunc_name);

			address = sc_dlsym(handle, name);
			if (address)
				init_func_ex = (int (*)(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *)) address;
		}
	}
	/* try to initialize the pkcs15 structures */
	if (init_func_ex)
		r = init_func_ex(p15card, &opts);
	else if (init_func)
		r = init_func(p15card);
	else
		r = SC_ERROR_WRONG_CARD;

	if (r >= 0) {
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s succeeded, card bound\n",
				module_name);
		p15card->dll_handle = handle;
	} else {
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%s failed: %s\n",
				module_name, sc_strerror(r));
		/* clear pkcs15 card */
		sc_pkcs15_card_clear(p15card);
		if (handle)
			sc_dlclose(handle);
	}

	return r;
}
예제 #7
-1
파일: card.c 프로젝트: DDvO/OpenSC
static int
sc_card_sm_load(struct sc_card *card, const char *module_path, const char *in_module)
{
	struct sc_context *ctx = NULL;
	int rv = SC_ERROR_INTERNAL;
	char *module = NULL;
#ifdef _WIN32
	char temp_path[PATH_MAX];
	int temp_len;
	long rc;
	HKEY hKey;
	const char path_delim = '\\';
#else
	const char path_delim = '/';
#endif

	assert(card != NULL);
	ctx = card->ctx;
	SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL);
	if (!in_module)
		return sc_card_sm_unload(card);

#ifdef _WIN32
	if (!module_path) {
		rc = RegOpenKeyExA( HKEY_CURRENT_USER, "Software\\OpenSC Project\\OpenSC", 0, KEY_QUERY_VALUE, &hKey );
		if( rc == ERROR_SUCCESS ) {
			temp_len = PATH_MAX;
			rc = RegQueryValueExA( hKey, "SmDir", NULL, NULL, (LPBYTE) temp_path, &temp_len);
			if( (rc == ERROR_SUCCESS) && (temp_len < PATH_MAX) )
				module_path = temp_path;
			RegCloseKey( hKey );
		}
	}
	if (!module_path) {
		rc = RegOpenKeyExA( HKEY_LOCAL_MACHINE, "Software\\OpenSC Project\\OpenSC", 0, KEY_QUERY_VALUE, &hKey );
		if( rc == ERROR_SUCCESS ) {
			temp_len = PATH_MAX;
			rc = RegQueryValueExA( hKey, "SmDir", NULL, NULL, (LPBYTE) temp_path, &temp_len);
			if(rc == ERROR_SUCCESS && temp_len < PATH_MAX)
				module_path = temp_path;
			RegCloseKey( hKey );
		}
	}
#endif
	sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "SM module '%s' located in '%s'", in_module, module_path);
	if (module_path)   {
		int sz = strlen(in_module) + strlen(module_path) + 3;
		module = malloc(sz);
		if (module)
			snprintf(module, sz, "%s%c%s", module_path, path_delim, in_module);
	}
	else   {
		module = strdup(in_module);
	}

	if (!module)
		return SC_ERROR_OUT_OF_MEMORY;

	sc_log(ctx, "try to load SM module '%s'", module);
	do  {
		struct sm_module_operations *mod_ops = &card->sm_ctx.module.ops;
		void *mod_handle;

		card->sm_ctx.module.handle = sc_dlopen(module);
		if (!card->sm_ctx.module.handle)   {
			sc_log(ctx, "cannot open dynamic library '%s': %s", module, sc_dlerror());
			break;
		}
		mod_handle = card->sm_ctx.module.handle;

		mod_ops->initialize = sc_dlsym(mod_handle, "initialize");
		if (!mod_ops->initialize)   {
			sc_log(ctx, "SM handler 'initialize' not exported: %s", sc_dlerror());
			break;
		}

		mod_ops->get_apdus  = sc_dlsym(mod_handle, "get_apdus");
		if (!mod_ops->get_apdus)   {
			sc_log(ctx, "SM handler 'get_apdus' not exported: %s", sc_dlerror());
			break;
		}

		mod_ops->finalize  = sc_dlsym(mod_handle, "finalize");
		if (!mod_ops->finalize)
			sc_log(ctx, "SM handler 'finalize' not exported -- ignored");

		mod_ops->module_init  = sc_dlsym(mod_handle, "module_init");
		if (!mod_ops->module_init)
			sc_log(ctx, "SM handler 'module_init' not exported -- ignored");

		mod_ops->module_cleanup  = sc_dlsym(mod_handle, "module_cleanup");
		if (!mod_ops->module_cleanup)
			sc_log(ctx, "SM handler 'module_cleanup' not exported -- ignored");

		mod_ops->test  = sc_dlsym(mod_handle, "test");
		if (mod_ops->test)
			sc_log(ctx, "SM handler 'test' not exported -- ignored");

		rv = 0;
		break;
	} while(0);

	if (rv)
		sc_card_sm_unload(card);

	card->sm_ctx.sm_mode = SM_MODE_ACL;
	if (module)
		free(module);

	SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, rv);
}