2002-03-24 22:47:35 +00:00
|
|
|
/*
|
|
|
|
* ctx.c: Context related functions
|
|
|
|
*
|
2006-12-19 21:32:31 +00:00
|
|
|
* Copyright (C) 2002 Juha Yrjölä <juha.yrjola@iki.fi>
|
2002-03-24 22:47:35 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
2015-04-22 21:55:33 +00:00
|
|
|
#if HAVE_CONFIG_H
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "config.h"
|
2015-04-22 21:55:33 +00:00
|
|
|
#endif
|
2010-03-04 08:14:36 +00:00
|
|
|
|
2002-03-24 22:47:35 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
2003-01-09 09:18:02 +00:00
|
|
|
#include <errno.h>
|
2003-08-29 16:26:59 +00:00
|
|
|
#include <sys/stat.h>
|
2003-01-09 09:18:02 +00:00
|
|
|
#include <limits.h>
|
2004-10-18 08:24:12 +00:00
|
|
|
|
2005-03-23 23:10:50 +00:00
|
|
|
#ifdef _WIN32
|
2010-03-09 13:39:29 +00:00
|
|
|
#include <windows.h>
|
2005-03-23 23:10:50 +00:00
|
|
|
#include <winreg.h>
|
2015-10-12 06:24:46 +00:00
|
|
|
#include <direct.h>
|
2005-03-23 23:10:50 +00:00
|
|
|
#endif
|
|
|
|
|
2011-12-09 20:46:45 +00:00
|
|
|
#include "common/libscdl.h"
|
2010-03-09 13:39:29 +00:00
|
|
|
#include "internal.h"
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
int _sc_add_reader(sc_context_t *ctx, sc_reader_t *reader)
|
2002-03-24 22:47:35 +00:00
|
|
|
{
|
|
|
|
assert(reader != NULL);
|
|
|
|
reader->ctx = ctx;
|
2010-09-11 13:01:06 +00:00
|
|
|
list_append(&ctx->readers, reader);
|
2005-02-20 08:26:27 +00:00
|
|
|
return SC_SUCCESS;
|
2002-03-24 22:47:35 +00:00
|
|
|
}
|
|
|
|
|
2011-02-03 21:18:26 +00:00
|
|
|
int _sc_delete_reader(sc_context_t *ctx, sc_reader_t *reader)
|
|
|
|
{
|
|
|
|
assert(reader != NULL);
|
|
|
|
if (reader->ops->release)
|
|
|
|
reader->ops->release(reader);
|
|
|
|
if (reader->name)
|
|
|
|
free(reader->name);
|
2016-05-26 12:36:55 +00:00
|
|
|
if (reader->vendor)
|
|
|
|
free(reader->vendor);
|
2011-02-03 21:18:26 +00:00
|
|
|
list_delete(&ctx->readers, reader);
|
|
|
|
free(reader);
|
2011-02-05 17:20:43 +00:00
|
|
|
return SC_SUCCESS;
|
2011-02-03 21:18:26 +00:00
|
|
|
}
|
|
|
|
|
2002-03-24 22:47:35 +00:00
|
|
|
struct _sc_driver_entry {
|
2010-03-28 21:02:56 +00:00
|
|
|
const char *name;
|
2005-09-11 20:40:58 +00:00
|
|
|
void *(*func)(void);
|
2002-03-24 22:47:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct _sc_driver_entry internal_card_drivers[] = {
|
2006-01-23 21:44:37 +00:00
|
|
|
{ "cardos", (void *(*)(void)) sc_get_cardos_driver },
|
2005-09-15 05:41:29 +00:00
|
|
|
{ "flex", (void *(*)(void)) sc_get_cryptoflex_driver },
|
|
|
|
{ "cyberflex", (void *(*)(void)) sc_get_cyberflex_driver },
|
2008-03-06 16:06:59 +00:00
|
|
|
#ifdef ENABLE_OPENSSL
|
2005-09-15 05:41:29 +00:00
|
|
|
{ "gpk", (void *(*)(void)) sc_get_gpk_driver },
|
2002-03-24 22:47:35 +00:00
|
|
|
#endif
|
2007-11-12 10:18:54 +00:00
|
|
|
{ "gemsafeV1", (void *(*)(void)) sc_get_gemsafeV1_driver },
|
2005-09-15 05:41:29 +00:00
|
|
|
{ "miocos", (void *(*)(void)) sc_get_miocos_driver },
|
2007-07-03 20:44:34 +00:00
|
|
|
{ "asepcos", (void *(*)(void)) sc_get_asepcos_driver },
|
2005-09-15 05:41:29 +00:00
|
|
|
{ "starcos", (void *(*)(void)) sc_get_starcos_driver },
|
|
|
|
{ "tcos", (void *(*)(void)) sc_get_tcos_driver },
|
|
|
|
{ "jcop", (void *(*)(void)) sc_get_jcop_driver },
|
2008-03-06 16:06:59 +00:00
|
|
|
#ifdef ENABLE_OPENSSL
|
2005-09-15 05:41:29 +00:00
|
|
|
{ "oberthur", (void *(*)(void)) sc_get_oberthur_driver },
|
2010-12-30 14:40:28 +00:00
|
|
|
{ "authentic", (void *(*)(void)) sc_get_authentic_driver },
|
2011-02-16 10:59:10 +00:00
|
|
|
{ "iasecc", (void *(*)(void)) sc_get_iasecc_driver },
|
2004-06-16 20:59:59 +00:00
|
|
|
#endif
|
2005-09-15 05:41:29 +00:00
|
|
|
{ "belpic", (void *(*)(void)) sc_get_belpic_driver },
|
2005-10-24 21:58:35 +00:00
|
|
|
{ "incrypto34", (void *(*)(void)) sc_get_incrypto34_driver },
|
2007-03-13 13:38:24 +00:00
|
|
|
{ "acos5", (void *(*)(void)) sc_get_acos5_driver },
|
2007-07-17 20:01:55 +00:00
|
|
|
{ "akis", (void *(*)(void)) sc_get_akis_driver },
|
2008-08-20 05:41:20 +00:00
|
|
|
#ifdef ENABLE_OPENSSL
|
|
|
|
{ "entersafe",(void *(*)(void)) sc_get_entersafe_driver },
|
2012-06-04 09:52:50 +00:00
|
|
|
#ifdef ENABLE_SM
|
|
|
|
{ "epass2003",(void *(*)(void)) sc_get_epass2003_driver },
|
|
|
|
#endif
|
2008-08-20 05:41:20 +00:00
|
|
|
#endif
|
2010-09-11 13:01:06 +00:00
|
|
|
{ "rutoken", (void *(*)(void)) sc_get_rutoken_driver },
|
2009-06-24 15:26:37 +00:00
|
|
|
{ "rutoken_ecp",(void *(*)(void)) sc_get_rtecp_driver },
|
2009-09-12 11:46:00 +00:00
|
|
|
{ "westcos", (void *(*)(void)) sc_get_westcos_driver },
|
2010-08-16 08:59:09 +00:00
|
|
|
{ "myeid", (void *(*)(void)) sc_get_myeid_driver },
|
2012-07-31 12:57:00 +00:00
|
|
|
{ "sc-hsm", (void *(*)(void)) sc_get_sc_hsm_driver },
|
2016-12-30 10:30:02 +00:00
|
|
|
#if defined(ENABLE_OPENSSL) && defined(ENABLE_SM)
|
2013-06-13 15:12:07 +00:00
|
|
|
{ "dnie", (void *(*)(void)) sc_get_dnie_driver },
|
|
|
|
#endif
|
2015-05-02 10:37:50 +00:00
|
|
|
{ "masktech", (void *(*)(void)) sc_get_masktech_driver },
|
2010-02-11 14:15:13 +00:00
|
|
|
|
2010-09-11 13:01:06 +00:00
|
|
|
/* Here should be placed drivers that need some APDU transactions to
|
2010-02-11 14:15:13 +00:00
|
|
|
* recognise its cards. */
|
2015-03-20 17:08:18 +00:00
|
|
|
{ "mcrd", (void *(*)(void)) sc_get_mcrd_driver },
|
2010-02-11 14:15:13 +00:00
|
|
|
{ "setcos", (void *(*)(void)) sc_get_setcos_driver },
|
|
|
|
{ "muscle", (void *(*)(void)) sc_get_muscle_driver },
|
|
|
|
{ "atrust-acos",(void *(*)(void)) sc_get_atrust_acos_driver },
|
|
|
|
{ "PIV-II", (void *(*)(void)) sc_get_piv_driver },
|
2017-02-27 10:05:12 +00:00
|
|
|
{ "cac", (void *(*)(void)) sc_get_cac_driver },
|
2010-08-16 08:59:09 +00:00
|
|
|
{ "itacns", (void *(*)(void)) sc_get_itacns_driver },
|
2014-08-28 12:29:16 +00:00
|
|
|
{ "isoApplet", (void *(*)(void)) sc_get_isoApplet_driver },
|
2016-04-07 17:42:16 +00:00
|
|
|
#ifdef ENABLE_ZLIB
|
2015-12-27 12:06:16 +00:00
|
|
|
{ "gids", (void *(*)(void)) sc_get_gids_driver },
|
2016-04-07 17:42:16 +00:00
|
|
|
#endif
|
2015-07-28 22:26:19 +00:00
|
|
|
{ "openpgp", (void *(*)(void)) sc_get_openpgp_driver },
|
2016-06-16 07:35:54 +00:00
|
|
|
{ "jpki", (void *(*)(void)) sc_get_jpki_driver },
|
2016-10-14 08:45:56 +00:00
|
|
|
{ "coolkey", (void *(*)(void)) sc_get_coolkey_driver },
|
2002-03-24 22:47:35 +00:00
|
|
|
/* The default driver should be last, as it handles all the
|
|
|
|
* unrecognized cards. */
|
2005-09-15 05:41:29 +00:00
|
|
|
{ "default", (void *(*)(void)) sc_get_default_driver },
|
2005-02-20 08:26:27 +00:00
|
|
|
{ NULL, NULL }
|
2002-03-24 22:47:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct _sc_ctx_options {
|
2005-02-20 08:26:27 +00:00
|
|
|
struct _sc_driver_entry cdrv[SC_MAX_CARD_DRIVERS];
|
2002-03-24 22:47:35 +00:00
|
|
|
int ccount;
|
2002-04-19 20:07:56 +00:00
|
|
|
char *forced_card_driver;
|
2002-03-24 22:47:35 +00:00
|
|
|
};
|
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
|
2016-04-13 12:27:26 +00:00
|
|
|
int
|
|
|
|
sc_ctx_win32_get_config_value(char *name_env, char *name_reg, char *name_key,
|
|
|
|
char *out, size_t *out_len)
|
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
char temp[PATH_MAX + 1];
|
|
|
|
char *value = NULL;
|
|
|
|
int temp_len = PATH_MAX;
|
|
|
|
int rv = SC_ERROR_INTERNAL;
|
|
|
|
long rc;
|
|
|
|
HKEY hKey;
|
|
|
|
|
|
|
|
if (!out || !out_len)
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
|
|
|
if (name_env) {
|
|
|
|
value = getenv(name_env);
|
|
|
|
if (value)
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!name_reg)
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
|
|
|
if (!name_key)
|
|
|
|
name_key = "Software\\OpenSC Project\\OpenSC";
|
|
|
|
|
|
|
|
rc = RegOpenKeyExA(HKEY_CURRENT_USER, name_key, 0, KEY_QUERY_VALUE, &hKey);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
temp_len = PATH_MAX;
|
|
|
|
rc = RegQueryValueEx( hKey, name_reg, NULL, NULL, (LPBYTE) temp, &temp_len);
|
|
|
|
if ((rc == ERROR_SUCCESS) && (temp_len < PATH_MAX))
|
|
|
|
value = temp;
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!value) {
|
|
|
|
rc = RegOpenKeyExA( HKEY_LOCAL_MACHINE, name_key, 0, KEY_QUERY_VALUE, &hKey );
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
|
|
temp_len = PATH_MAX;
|
|
|
|
rc = RegQueryValueEx( hKey, name_reg, NULL, NULL, (LPBYTE) temp, &temp_len);
|
|
|
|
if ((rc == ERROR_SUCCESS) && (temp_len < PATH_MAX))
|
|
|
|
value = temp;
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (value) {
|
|
|
|
if (strlen(value) >= *out_len)
|
|
|
|
return SC_ERROR_BUFFER_TOO_SMALL;
|
|
|
|
strcpy(out, value);
|
|
|
|
*out_len = strlen(out);
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(out, 0, *out_len);
|
|
|
|
*out_len = 0;
|
|
|
|
|
|
|
|
return SC_ERROR_OBJECT_NOT_FOUND;
|
|
|
|
#else
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
/* Simclist helper to locate readers by name */
|
|
|
|
static int reader_list_seeker(const void *el, const void *key) {
|
2010-09-11 13:01:06 +00:00
|
|
|
const struct sc_reader *reader = (struct sc_reader *)el;
|
|
|
|
if ((el == NULL) || (key == NULL))
|
|
|
|
return 0;
|
|
|
|
if (strcmp(reader->name, (char*)key) == 0)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
2010-01-24 15:25:08 +00:00
|
|
|
}
|
2010-09-11 13:01:06 +00:00
|
|
|
|
2010-09-11 13:00:47 +00:00
|
|
|
static void del_drvs(struct _sc_ctx_options *opts)
|
2002-03-24 22:47:35 +00:00
|
|
|
{
|
|
|
|
struct _sc_driver_entry *lst;
|
|
|
|
int *cp, i;
|
2005-02-06 19:40:40 +00:00
|
|
|
|
2010-09-11 13:00:47 +00:00
|
|
|
lst = opts->cdrv;
|
|
|
|
cp = &opts->ccount;
|
|
|
|
|
2002-03-24 22:47:35 +00:00
|
|
|
for (i = 0; i < *cp; i++) {
|
2010-03-28 21:02:56 +00:00
|
|
|
free((void *)lst[i].name);
|
2002-03-24 22:47:35 +00:00
|
|
|
}
|
|
|
|
*cp = 0;
|
|
|
|
}
|
|
|
|
|
2010-09-11 13:00:47 +00:00
|
|
|
static void add_drv(struct _sc_ctx_options *opts, const char *name)
|
2002-03-24 22:47:35 +00:00
|
|
|
{
|
|
|
|
struct _sc_driver_entry *lst;
|
2005-02-20 08:26:27 +00:00
|
|
|
int *cp, max, i;
|
2012-02-17 09:06:57 +00:00
|
|
|
|
2010-09-11 13:00:47 +00:00
|
|
|
lst = opts->cdrv;
|
|
|
|
cp = &opts->ccount;
|
|
|
|
max = SC_MAX_CARD_DRIVERS;
|
2005-02-20 08:26:27 +00:00
|
|
|
if (*cp == max) /* No space for more drivers... */
|
2002-03-24 22:47:35 +00:00
|
|
|
return;
|
|
|
|
for (i = 0; i < *cp; i++)
|
|
|
|
if (strcmp(name, lst[i].name) == 0)
|
|
|
|
return;
|
|
|
|
lst[*cp].name = strdup(name);
|
|
|
|
|
|
|
|
*cp = *cp + 1;
|
|
|
|
}
|
|
|
|
|
2010-09-11 13:00:47 +00:00
|
|
|
static void add_internal_drvs(struct _sc_ctx_options *opts)
|
2002-03-24 22:47:35 +00:00
|
|
|
{
|
|
|
|
const struct _sc_driver_entry *lst;
|
|
|
|
int i;
|
2005-02-06 19:40:40 +00:00
|
|
|
|
2010-09-11 13:00:47 +00:00
|
|
|
lst = internal_card_drivers;
|
2002-03-24 22:47:35 +00:00
|
|
|
i = 0;
|
|
|
|
while (lst[i].name != NULL) {
|
2010-09-11 13:00:47 +00:00
|
|
|
add_drv(opts, lst[i].name);
|
2002-03-24 22:47:35 +00:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static void set_defaults(sc_context_t *ctx, struct _sc_ctx_options *opts)
|
2002-03-28 14:13:36 +00:00
|
|
|
{
|
|
|
|
ctx->debug = 0;
|
2009-09-15 12:29:17 +00:00
|
|
|
if (ctx->debug_file && (ctx->debug_file != stderr && ctx->debug_file != stdout))
|
2002-03-28 14:13:36 +00:00
|
|
|
fclose(ctx->debug_file);
|
2009-09-14 08:46:59 +00:00
|
|
|
ctx->debug_file = stderr;
|
2015-07-22 15:25:35 +00:00
|
|
|
ctx->flags = 0;
|
2013-08-02 20:01:51 +00:00
|
|
|
|
2010-06-16 14:12:27 +00:00
|
|
|
#ifdef __APPLE__
|
|
|
|
/* Override the default debug log for OpenSC.tokend to be different from PKCS#11.
|
|
|
|
* TODO: Could be moved to OpenSC.tokend */
|
|
|
|
if (!strcmp(ctx->app_name, "tokend"))
|
|
|
|
ctx->debug_file = fopen("/tmp/opensc-tokend.log", "a");
|
|
|
|
#endif
|
2002-03-28 14:13:36 +00:00
|
|
|
ctx->forced_driver = NULL;
|
2010-09-11 13:00:47 +00:00
|
|
|
add_internal_drvs(opts);
|
2002-03-28 14:13:36 +00:00
|
|
|
}
|
|
|
|
|
2011-04-18 10:01:27 +00:00
|
|
|
/* In Windows, file handles can not be shared between DLL-s,
|
|
|
|
* each DLL has a separate file handle table. Thus tools and utilities
|
|
|
|
* can not set the file handle themselves when -v is specified on command line.
|
|
|
|
*/
|
|
|
|
int sc_ctx_log_to_file(sc_context_t *ctx, const char* filename)
|
|
|
|
{
|
|
|
|
/* Close any existing handles */
|
2013-08-02 10:22:42 +00:00
|
|
|
if (ctx->debug_file && (ctx->debug_file != stderr && ctx->debug_file != stdout)) {
|
2011-04-18 10:01:27 +00:00
|
|
|
fclose(ctx->debug_file);
|
2013-08-02 10:22:42 +00:00
|
|
|
ctx->debug_file = NULL;
|
|
|
|
}
|
2011-04-18 10:01:27 +00:00
|
|
|
|
tools: fix segfault with verbose log into 'stderr'
Issue #824
In Windows, file handles (including 'stderr', 'stdout') can not be shared
between DLL-s, and so, the log handle (File *), defined in one module, cannot
be reused in another.
That is the situation when, for example, the SM is processed
in external, dynamically loadable module as it currently implemented for
IAS/ECC card.
That's for the configuration option 're-open of log file on each message' was
introduced.
This 're-open' logic has not been tested in the particular case of opensc-*
tools used with verbose log into 'stderr' -- in dynamically loaded module the
'stderr' handle, defined in the 'main' module, was not recognized as 'stderr'
and there was an attempt to close it.
closes #910
2016-11-23 10:24:41 +00:00
|
|
|
if (ctx->reopen_log_file) {
|
|
|
|
if (!ctx->debug_filename) {
|
|
|
|
if (!filename)
|
|
|
|
filename = "stderr";
|
|
|
|
ctx->debug_filename = strdup(filename);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!filename)
|
|
|
|
return SC_SUCCESS;
|
|
|
|
|
2011-04-18 10:01:27 +00:00
|
|
|
/* Handle special names */
|
|
|
|
if (!strcmp(filename, "stdout"))
|
|
|
|
ctx->debug_file = stdout;
|
|
|
|
else if (!strcmp(filename, "stderr"))
|
|
|
|
ctx->debug_file = stderr;
|
|
|
|
else {
|
|
|
|
ctx->debug_file = fopen(filename, "a");
|
|
|
|
if (ctx->debug_file == NULL)
|
|
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
}
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2012-05-29 17:44:54 +00:00
|
|
|
|
|
|
|
static int
|
|
|
|
load_parameters(sc_context_t *ctx, scconf_block *block, struct _sc_ctx_options *opts)
|
2002-03-24 22:47:35 +00:00
|
|
|
{
|
2002-03-26 11:38:40 +00:00
|
|
|
int err = 0;
|
|
|
|
const scconf_list *list;
|
2005-02-20 08:26:27 +00:00
|
|
|
const char *val, *s_internal = "internal";
|
2011-10-04 18:05:44 +00:00
|
|
|
int debug;
|
2012-05-29 17:44:54 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
char expanded_val[PATH_MAX];
|
|
|
|
DWORD expanded_len;
|
|
|
|
#endif
|
2011-10-04 18:05:44 +00:00
|
|
|
|
tools: fix segfault with verbose log into 'stderr'
Issue #824
In Windows, file handles (including 'stderr', 'stdout') can not be shared
between DLL-s, and so, the log handle (File *), defined in one module, cannot
be reused in another.
That is the situation when, for example, the SM is processed
in external, dynamically loadable module as it currently implemented for
IAS/ECC card.
That's for the configuration option 're-open of log file on each message' was
introduced.
This 're-open' logic has not been tested in the particular case of opensc-*
tools used with verbose log into 'stderr' -- in dynamically loaded module the
'stderr' handle, defined in the 'main' module, was not recognized as 'stderr'
and there was an attempt to close it.
closes #910
2016-11-23 10:24:41 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
ctx->reopen_log_file = 1;
|
|
|
|
#else
|
|
|
|
ctx->reopen_log_file = scconf_get_bool(block, "reopen_debug_file", 0);
|
|
|
|
#endif
|
2012-05-29 17:44:54 +00:00
|
|
|
|
2011-10-04 18:05:44 +00:00
|
|
|
debug = scconf_get_int(block, "debug", ctx->debug);
|
|
|
|
if (debug > ctx->debug)
|
|
|
|
ctx->debug = debug;
|
2006-10-30 07:37:44 +00:00
|
|
|
|
2002-03-26 11:38:40 +00:00
|
|
|
val = scconf_get_str(block, "debug_file", NULL);
|
2012-05-29 17:44:54 +00:00
|
|
|
if (val) {
|
|
|
|
#ifdef _WIN32
|
|
|
|
expanded_len = PATH_MAX;
|
2015-03-28 09:00:47 +00:00
|
|
|
expanded_len = ExpandEnvironmentStringsA(val, expanded_val, expanded_len);
|
2012-05-29 17:44:54 +00:00
|
|
|
if (expanded_len > 0)
|
|
|
|
val = expanded_val;
|
|
|
|
#endif
|
2011-04-18 10:01:27 +00:00
|
|
|
sc_ctx_log_to_file(ctx, val);
|
2012-05-29 17:44:54 +00:00
|
|
|
}
|
tools: fix segfault with verbose log into 'stderr'
Issue #824
In Windows, file handles (including 'stderr', 'stdout') can not be shared
between DLL-s, and so, the log handle (File *), defined in one module, cannot
be reused in another.
That is the situation when, for example, the SM is processed
in external, dynamically loadable module as it currently implemented for
IAS/ECC card.
That's for the configuration option 're-open of log file on each message' was
introduced.
This 're-open' logic has not been tested in the particular case of opensc-*
tools used with verbose log into 'stderr' -- in dynamically loaded module the
'stderr' handle, defined in the 'main' module, was not recognized as 'stderr'
and there was an attempt to close it.
closes #910
2016-11-23 10:24:41 +00:00
|
|
|
else if (ctx->debug) {
|
|
|
|
sc_ctx_log_to_file(ctx, NULL);
|
|
|
|
}
|
2011-04-18 10:01:27 +00:00
|
|
|
|
2015-07-22 15:25:35 +00:00
|
|
|
if (scconf_get_bool (block, "paranoid-memory",
|
2016-01-21 17:11:42 +00:00
|
|
|
ctx->flags & SC_CTX_FLAG_PARANOID_MEMORY))
|
2015-07-22 15:25:35 +00:00
|
|
|
ctx->flags |= SC_CTX_FLAG_PARANOID_MEMORY;
|
2011-12-14 12:14:14 +00:00
|
|
|
|
2016-12-09 11:54:06 +00:00
|
|
|
if (scconf_get_bool (block, "disable_popups",
|
|
|
|
ctx->flags & SC_CTX_FLAG_DISABLE_POPUPS))
|
|
|
|
ctx->flags |= SC_CTX_FLAG_DISABLE_POPUPS;
|
|
|
|
|
2015-07-22 15:25:35 +00:00
|
|
|
if (scconf_get_bool (block, "enable_default_driver",
|
2016-01-21 17:11:42 +00:00
|
|
|
ctx->flags & SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER))
|
2015-07-22 15:25:35 +00:00
|
|
|
ctx->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER;
|
2013-08-02 20:01:51 +00:00
|
|
|
|
2002-04-19 20:07:56 +00:00
|
|
|
val = scconf_get_str(block, "force_card_driver", NULL);
|
|
|
|
if (val) {
|
|
|
|
if (opts->forced_card_driver)
|
|
|
|
free(opts->forced_card_driver);
|
|
|
|
opts->forced_card_driver = strdup(val);
|
|
|
|
}
|
2002-03-24 22:47:35 +00:00
|
|
|
|
2002-03-26 11:38:40 +00:00
|
|
|
list = scconf_find_list(block, "card_drivers");
|
2002-03-28 14:13:36 +00:00
|
|
|
if (list != NULL)
|
2010-09-11 13:00:47 +00:00
|
|
|
del_drvs(opts);
|
2002-03-26 11:38:40 +00:00
|
|
|
while (list != NULL) {
|
|
|
|
if (strcmp(list->data, s_internal) == 0)
|
2010-09-11 13:00:47 +00:00
|
|
|
add_internal_drvs(opts);
|
2002-03-26 11:38:40 +00:00
|
|
|
else
|
2010-09-11 13:00:47 +00:00
|
|
|
add_drv(opts, list->data);
|
2002-03-26 11:38:40 +00:00
|
|
|
list = list->next;
|
2002-03-24 22:47:35 +00:00
|
|
|
}
|
2002-03-26 11:38:40 +00:00
|
|
|
|
2002-03-24 22:47:35 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2003-12-18 16:35:28 +00:00
|
|
|
|
2004-10-18 08:24:12 +00:00
|
|
|
/**
|
|
|
|
* find library module for provided driver in configuration file
|
|
|
|
* if not found assume library name equals to module name
|
|
|
|
*/
|
2010-09-11 13:00:47 +00:00
|
|
|
static const char *find_library(sc_context_t *ctx, const char *name)
|
2004-10-18 08:24:12 +00:00
|
|
|
{
|
2016-04-13 12:27:26 +00:00
|
|
|
int i, log_warning;
|
|
|
|
const char *libname = NULL;
|
2004-10-18 08:24:12 +00:00
|
|
|
scconf_block **blocks, *blk;
|
|
|
|
|
|
|
|
for (i = 0; ctx->conf_blocks[i]; i++) {
|
2010-09-11 13:00:47 +00:00
|
|
|
blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "card_driver", name);
|
2005-12-05 21:35:31 +00:00
|
|
|
if (!blocks)
|
|
|
|
continue;
|
2004-10-18 08:24:12 +00:00
|
|
|
blk = blocks[0];
|
|
|
|
free(blocks);
|
|
|
|
if (blk == NULL)
|
|
|
|
continue;
|
|
|
|
libname = scconf_get_str(blk, "module", name);
|
|
|
|
#ifdef _WIN32
|
2015-11-30 00:33:11 +00:00
|
|
|
log_warning = libname && libname[0] != '\\';
|
2004-10-18 08:24:12 +00:00
|
|
|
#else
|
2015-11-30 00:33:11 +00:00
|
|
|
log_warning = libname && libname[0] != '/';
|
2004-10-18 08:24:12 +00:00
|
|
|
#endif
|
2015-11-28 14:33:38 +00:00
|
|
|
if (log_warning)
|
2012-06-11 17:45:26 +00:00
|
|
|
sc_log(ctx, "warning: relative path to driver '%s' used", libname);
|
2004-10-18 08:24:12 +00:00
|
|
|
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
|
|
|
|
*/
|
2010-09-11 13:00:47 +00:00
|
|
|
static void *load_dynamic_driver(sc_context_t *ctx, void **dll, const char *name)
|
2004-10-18 08:24:12 +00:00
|
|
|
{
|
|
|
|
const char *version, *libname;
|
2011-02-16 19:02:11 +00:00
|
|
|
void *handle;
|
2004-10-18 08:24:12 +00:00
|
|
|
void *(*modinit)(const char *) = NULL;
|
2005-09-11 20:40:58 +00:00
|
|
|
void *(**tmodi)(const char *) = &modinit;
|
2004-10-18 08:24:12 +00:00
|
|
|
const char *(*modversion)(void) = NULL;
|
2005-09-11 20:40:58 +00:00
|
|
|
const char *(**tmodv)(void) = &modversion;
|
2004-10-18 08:24:12 +00:00
|
|
|
|
|
|
|
if (name == NULL) { /* should not occurr, but... */
|
2012-06-11 17:45:26 +00:00
|
|
|
sc_log(ctx, "No module specified", name);
|
2004-10-18 08:24:12 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2010-09-11 13:00:47 +00:00
|
|
|
libname = find_library(ctx, name);
|
2004-10-18 08:24:12 +00:00
|
|
|
if (libname == NULL)
|
|
|
|
return NULL;
|
2011-02-16 19:02:11 +00:00
|
|
|
handle = sc_dlopen(libname);
|
2005-09-01 14:01:58 +00:00
|
|
|
if (handle == NULL) {
|
2012-06-11 17:45:26 +00:00
|
|
|
sc_log(ctx, "Module %s: cannot load %s library: %s", name, libname, sc_dlerror());
|
2004-10-18 08:24:12 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2004-12-15 13:53:36 +00:00
|
|
|
|
2004-10-18 08:24:12 +00:00
|
|
|
/* verify correctness of module */
|
2011-02-16 19:02:11 +00:00
|
|
|
*(void **)tmodi = sc_dlsym(handle, "sc_module_init");
|
|
|
|
*(void **)tmodv = sc_dlsym(handle, "sc_driver_version");
|
2004-10-18 08:24:12 +00:00
|
|
|
if (modinit == NULL || modversion == NULL) {
|
2012-06-11 17:45:26 +00:00
|
|
|
sc_log(ctx, "dynamic library '%s' is not a OpenSC module",libname);
|
2011-02-16 19:02:11 +00:00
|
|
|
sc_dlclose(handle);
|
2004-10-18 08:24:12 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* verify module version */
|
|
|
|
version = modversion();
|
2008-04-29 17:01:19 +00:00
|
|
|
/* XXX: We really need to have ABI version for each interface */
|
|
|
|
if (version == NULL || strncmp(version, PACKAGE_VERSION, strlen(PACKAGE_VERSION)) != 0) {
|
2012-06-11 17:45:26 +00:00
|
|
|
sc_log(ctx, "dynamic library '%s': invalid module version", libname);
|
2011-02-16 19:02:11 +00:00
|
|
|
sc_dlclose(handle);
|
2004-10-18 08:24:12 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2012-06-11 17:45:26 +00:00
|
|
|
if (dll)
|
|
|
|
*dll = handle;
|
|
|
|
sc_log(ctx, "successfully loaded card driver '%s'", name);
|
2004-10-18 08:24:12 +00:00
|
|
|
return modinit(name);
|
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int load_card_driver_options(sc_context_t *ctx,
|
2016-04-13 12:27:26 +00:00
|
|
|
struct sc_card_driver *driver)
|
2002-12-03 15:40:40 +00:00
|
|
|
{
|
|
|
|
scconf_block **blocks, *blk;
|
2005-02-06 19:40:40 +00:00
|
|
|
int i;
|
2002-12-03 15:40:40 +00:00
|
|
|
|
|
|
|
for (i = 0; ctx->conf_blocks[i]; i++) {
|
2012-06-11 17:45:26 +00:00
|
|
|
blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i],
|
|
|
|
"card_driver", driver->short_name);
|
2005-12-05 21:35:31 +00:00
|
|
|
if (!blocks)
|
|
|
|
continue;
|
2002-12-03 15:40:40 +00:00
|
|
|
blk = blocks[0];
|
|
|
|
free(blocks);
|
|
|
|
|
|
|
|
if (blk == NULL)
|
|
|
|
continue;
|
|
|
|
|
2005-02-22 07:59:42 +00:00
|
|
|
/* no options at the moment */
|
2002-12-03 15:40:40 +00:00
|
|
|
}
|
2005-02-20 08:26:27 +00:00
|
|
|
return SC_SUCCESS;
|
2002-12-03 15:40:40 +00:00
|
|
|
}
|
|
|
|
|
2016-04-07 17:42:16 +00:00
|
|
|
static int load_card_drivers(sc_context_t *ctx, struct _sc_ctx_options *opts)
|
2002-03-24 22:47:35 +00:00
|
|
|
{
|
|
|
|
const struct _sc_driver_entry *ent;
|
|
|
|
int drv_count;
|
|
|
|
int i;
|
|
|
|
|
2012-08-16 12:18:08 +00:00
|
|
|
for (drv_count = 0; ctx->card_drivers[drv_count] != NULL; drv_count++)
|
|
|
|
;
|
2002-03-24 22:47:35 +00:00
|
|
|
|
|
|
|
for (i = 0; i < opts->ccount; i++) {
|
2004-12-15 13:53:36 +00:00
|
|
|
struct sc_card_driver *(*func)(void) = NULL;
|
2005-09-11 20:40:58 +00:00
|
|
|
struct sc_card_driver *(**tfunc)(void) = &func;
|
2004-10-18 08:24:12 +00:00
|
|
|
void *dll = NULL;
|
|
|
|
int j;
|
2002-03-24 22:47:35 +00:00
|
|
|
|
2012-08-16 12:18:08 +00:00
|
|
|
if (drv_count >= SC_MAX_CARD_DRIVERS - 1) {
|
|
|
|
sc_log(ctx, "Not more then %i card drivers allowed.", SC_MAX_CARD_DRIVERS);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2002-03-24 22:47:35 +00:00
|
|
|
ent = &opts->cdrv[i];
|
|
|
|
for (j = 0; internal_card_drivers[j].name != NULL; j++)
|
|
|
|
if (strcmp(ent->name, internal_card_drivers[j].name) == 0) {
|
2004-12-15 13:53:36 +00:00
|
|
|
func = (struct sc_card_driver *(*)(void)) internal_card_drivers[j].func;
|
2002-03-24 22:47:35 +00:00
|
|
|
break;
|
|
|
|
}
|
2004-10-18 08:24:12 +00:00
|
|
|
/* if not initialized assume external module */
|
|
|
|
if (func == NULL)
|
2010-09-11 13:00:47 +00:00
|
|
|
*(void **)(tfunc) = load_dynamic_driver(ctx, &dll, ent->name);
|
2004-10-18 08:24:12 +00:00
|
|
|
/* if still null, assume driver not found */
|
2002-03-24 22:47:35 +00:00
|
|
|
if (func == NULL) {
|
2012-06-11 17:45:26 +00:00
|
|
|
sc_log(ctx, "Unable to load '%s'.", ent->name);
|
|
|
|
if (dll)
|
|
|
|
sc_dlclose(dll);
|
2002-03-24 22:47:35 +00:00
|
|
|
continue;
|
|
|
|
}
|
2004-10-18 08:24:12 +00:00
|
|
|
|
2002-03-24 22:47:35 +00:00
|
|
|
ctx->card_drivers[drv_count] = func();
|
2016-04-07 17:42:16 +00:00
|
|
|
if (ctx->card_drivers[drv_count] == NULL) {
|
|
|
|
sc_log(ctx, "Driver '%s' not available.", ent->name);
|
|
|
|
if (dll)
|
|
|
|
sc_dlclose(dll);
|
|
|
|
continue;
|
|
|
|
}
|
2002-12-03 15:40:40 +00:00
|
|
|
|
2016-04-07 17:42:16 +00:00
|
|
|
ctx->card_drivers[drv_count]->dll = dll;
|
2005-02-14 09:12:44 +00:00
|
|
|
ctx->card_drivers[drv_count]->atr_map = NULL;
|
|
|
|
ctx->card_drivers[drv_count]->natrs = 0;
|
|
|
|
|
2002-12-03 15:40:40 +00:00
|
|
|
load_card_driver_options(ctx, ctx->card_drivers[drv_count]);
|
2012-08-16 12:18:08 +00:00
|
|
|
|
|
|
|
/* Ensure that the list is always terminated by NULL */
|
|
|
|
ctx->card_drivers[drv_count + 1] = NULL;
|
|
|
|
|
2005-02-20 08:26:27 +00:00
|
|
|
drv_count++;
|
2002-03-24 22:47:35 +00:00
|
|
|
}
|
2005-02-20 08:26:27 +00:00
|
|
|
return SC_SUCCESS;
|
2002-12-03 15:40:40 +00:00
|
|
|
}
|
2002-03-24 22:47:35 +00:00
|
|
|
|
2010-09-11 13:01:00 +00:00
|
|
|
static int load_card_atrs(sc_context_t *ctx)
|
2005-02-22 07:59:42 +00:00
|
|
|
{
|
|
|
|
struct sc_card_driver *driver;
|
|
|
|
scconf_block **blocks;
|
|
|
|
int i, j, k;
|
|
|
|
|
|
|
|
for (i = 0; ctx->conf_blocks[i] != NULL; i++) {
|
|
|
|
blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "card_atr", NULL);
|
|
|
|
if (!blocks)
|
|
|
|
continue;
|
|
|
|
for (j = 0; blocks[j] != NULL; j++) {
|
|
|
|
scconf_block *b = blocks[j];
|
|
|
|
char *atr = b->name->data;
|
|
|
|
const scconf_list *list;
|
|
|
|
struct sc_atr_table t;
|
|
|
|
const char *dname;
|
|
|
|
|
|
|
|
driver = NULL;
|
|
|
|
|
|
|
|
if (strlen(atr) < 4)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* The interesting part. If there's no card
|
|
|
|
* driver assigned for the ATR, add it to
|
|
|
|
* the default driver. This will reduce the
|
|
|
|
* amount of code required to process things
|
|
|
|
* related to card_atr blocks in situations,
|
|
|
|
* where the code is not exactly related to
|
|
|
|
* card driver settings, but for example
|
|
|
|
* forcing a protocol at the reader driver.
|
|
|
|
*/
|
|
|
|
dname = scconf_get_str(b, "driver", "default");
|
|
|
|
|
|
|
|
/* Find the card driver structure according to dname */
|
|
|
|
for (k = 0; ctx->card_drivers[k] != NULL; k++) {
|
|
|
|
driver = ctx->card_drivers[k];
|
|
|
|
if (!strcmp(dname, driver->short_name))
|
|
|
|
break;
|
|
|
|
driver = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!driver)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
memset(&t, 0, sizeof(struct sc_atr_table));
|
|
|
|
t.atr = atr;
|
|
|
|
t.atrmask = (char *) scconf_get_str(b, "atrmask", NULL);
|
|
|
|
t.name = (char *) scconf_get_str(b, "name", NULL);
|
2011-04-11 10:34:55 +00:00
|
|
|
t.type = scconf_get_int(b, "type", SC_CARD_TYPE_UNKNOWN);
|
2005-02-22 07:59:42 +00:00
|
|
|
list = scconf_find_list(b, "flags");
|
|
|
|
while (list != NULL) {
|
2012-06-11 17:45:26 +00:00
|
|
|
unsigned int flags = 0;
|
2005-02-22 07:59:42 +00:00
|
|
|
|
|
|
|
if (!list->data) {
|
|
|
|
list = list->next;
|
|
|
|
continue;
|
|
|
|
}
|
2012-06-11 17:45:26 +00:00
|
|
|
|
|
|
|
if (!strcmp(list->data, "rng"))
|
2005-02-22 07:59:42 +00:00
|
|
|
flags = SC_CARD_FLAG_RNG;
|
2012-06-11 17:45:26 +00:00
|
|
|
else if (sscanf(list->data, "%x", &flags) != 1)
|
|
|
|
flags = 0;
|
|
|
|
|
2005-02-22 07:59:42 +00:00
|
|
|
t.flags |= flags;
|
|
|
|
list = list->next;
|
|
|
|
}
|
|
|
|
t.card_atr = b;
|
|
|
|
_sc_add_atr(ctx, driver, &t);
|
|
|
|
}
|
|
|
|
free(blocks);
|
|
|
|
}
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static void process_config_file(sc_context_t *ctx, struct _sc_ctx_options *opts)
|
2002-03-26 11:38:40 +00:00
|
|
|
{
|
|
|
|
int i, r, count = 0;
|
|
|
|
scconf_block **blocks;
|
2006-02-17 21:06:31 +00:00
|
|
|
const char *conf_path = NULL;
|
2011-10-04 18:05:44 +00:00
|
|
|
const char *debug = NULL;
|
2003-04-23 11:46:07 +00:00
|
|
|
#ifdef _WIN32
|
2005-03-23 23:10:50 +00:00
|
|
|
char temp_path[PATH_MAX];
|
2016-06-05 01:04:20 +00:00
|
|
|
size_t temp_len;
|
2003-04-23 11:46:07 +00:00
|
|
|
#endif
|
|
|
|
|
2011-10-04 18:05:44 +00:00
|
|
|
/* Takes effect even when no config around */
|
|
|
|
debug = getenv("OPENSC_DEBUG");
|
|
|
|
if (debug)
|
|
|
|
ctx->debug = atoi(debug);
|
|
|
|
|
2002-12-21 14:10:36 +00:00
|
|
|
memset(ctx->conf_blocks, 0, sizeof(ctx->conf_blocks));
|
2003-01-20 10:12:28 +00:00
|
|
|
#ifdef _WIN32
|
2016-04-13 12:27:26 +00:00
|
|
|
temp_len = PATH_MAX;
|
|
|
|
r = sc_ctx_win32_get_config_value("OPENSC_CONF", "ConfigFile", "Software\\OpenSC Project\\OpenSC",
|
|
|
|
temp_path, &temp_len);
|
|
|
|
if (r) {
|
2012-06-11 17:45:26 +00:00
|
|
|
sc_log(ctx, "process_config_file doesn't find opensc config file. Please set the registry key.");
|
2006-01-04 18:52:52 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-04-13 12:27:26 +00:00
|
|
|
conf_path = temp_path;
|
2005-03-07 14:00:31 +00:00
|
|
|
#else
|
2010-09-11 13:01:06 +00:00
|
|
|
conf_path = getenv("OPENSC_CONF");
|
|
|
|
if (!conf_path)
|
|
|
|
conf_path = OPENSC_CONF_PATH;
|
2003-01-20 10:12:28 +00:00
|
|
|
#endif
|
|
|
|
ctx->conf = scconf_new(conf_path);
|
2002-03-26 11:38:40 +00:00
|
|
|
if (ctx->conf == NULL)
|
|
|
|
return;
|
2003-12-03 14:09:15 +00:00
|
|
|
r = scconf_parse(ctx->conf);
|
2003-01-03 13:26:58 +00:00
|
|
|
#ifdef OPENSC_CONFIG_STRING
|
2003-12-03 12:07:01 +00:00
|
|
|
/* Parse the string if config file didn't exist */
|
|
|
|
if (r < 0)
|
2003-01-03 13:26:58 +00:00
|
|
|
r = scconf_parse_string(ctx->conf, OPENSC_CONFIG_STRING);
|
|
|
|
#endif
|
2002-03-26 11:38:40 +00:00
|
|
|
if (r < 1) {
|
2003-12-03 12:07:01 +00:00
|
|
|
/* A negative return value means the config file isn't
|
|
|
|
* there, which is not an error. Nevertheless log this
|
|
|
|
* fact. */
|
|
|
|
if (r < 0)
|
2012-06-11 17:45:26 +00:00
|
|
|
sc_log(ctx, "scconf_parse failed: %s", ctx->conf->errmsg);
|
2003-12-03 12:07:01 +00:00
|
|
|
else
|
2012-06-11 17:45:26 +00:00
|
|
|
sc_log(ctx, "scconf_parse failed: %s", ctx->conf->errmsg);
|
2002-03-26 20:56:13 +00:00
|
|
|
scconf_free(ctx->conf);
|
2002-03-26 11:38:40 +00:00
|
|
|
ctx->conf = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
blocks = scconf_find_blocks(ctx->conf, NULL, "app", ctx->app_name);
|
|
|
|
if (blocks[0])
|
2012-02-17 09:06:57 +00:00
|
|
|
ctx->conf_blocks[count++] = blocks[0];
|
2002-03-26 11:38:40 +00:00
|
|
|
free(blocks);
|
|
|
|
if (strcmp(ctx->app_name, "default") != 0) {
|
|
|
|
blocks = scconf_find_blocks(ctx->conf, NULL, "app", "default");
|
|
|
|
if (blocks[0])
|
2012-02-17 09:06:57 +00:00
|
|
|
ctx->conf_blocks[count] = blocks[0];
|
2002-03-26 11:38:40 +00:00
|
|
|
free(blocks);
|
|
|
|
}
|
2002-12-21 14:10:36 +00:00
|
|
|
/* Above we add 2 blocks at most, but conf_blocks has 3 elements,
|
|
|
|
* so at least one is NULL */
|
|
|
|
for (i = 0; ctx->conf_blocks[i]; i++)
|
2002-03-26 11:38:40 +00:00
|
|
|
load_parameters(ctx, ctx->conf_blocks[i], opts);
|
|
|
|
}
|
|
|
|
|
2008-04-29 17:01:19 +00:00
|
|
|
int sc_ctx_detect_readers(sc_context_t *ctx)
|
|
|
|
{
|
2010-11-06 16:57:05 +00:00
|
|
|
int r = 0;
|
2010-09-11 13:00:47 +00:00
|
|
|
const struct sc_reader_driver *drv = ctx->reader_driver;
|
2008-04-29 17:01:19 +00:00
|
|
|
|
|
|
|
sc_mutex_lock(ctx, ctx->mutex);
|
|
|
|
|
2010-09-11 13:00:47 +00:00
|
|
|
if (drv->ops->detect_readers != NULL)
|
|
|
|
r = drv->ops->detect_readers(ctx);
|
2012-02-17 09:06:57 +00:00
|
|
|
|
2008-04-29 17:01:19 +00:00
|
|
|
sc_mutex_unlock(ctx, ctx->mutex);
|
|
|
|
|
2010-09-11 13:00:47 +00:00
|
|
|
return r;
|
2008-04-29 17:01:19 +00:00
|
|
|
}
|
|
|
|
|
2005-09-16 10:18:55 +00:00
|
|
|
sc_reader_t *sc_ctx_get_reader(sc_context_t *ctx, unsigned int i)
|
|
|
|
{
|
2010-09-11 13:01:06 +00:00
|
|
|
return list_get_at(&ctx->readers, i);
|
2010-01-24 15:25:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sc_reader_t *sc_ctx_get_reader_by_id(sc_context_t *ctx, unsigned int id)
|
|
|
|
{
|
2010-09-11 13:01:06 +00:00
|
|
|
return list_get_at(&ctx->readers, id);
|
2010-01-24 15:25:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sc_reader_t *sc_ctx_get_reader_by_name(sc_context_t *ctx, const char * name)
|
|
|
|
{
|
2010-09-11 13:01:06 +00:00
|
|
|
return list_seek(&ctx->readers, name);
|
2005-09-16 10:18:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int sc_ctx_get_reader_count(sc_context_t *ctx)
|
|
|
|
{
|
2010-01-24 15:25:08 +00:00
|
|
|
return list_size(&ctx->readers);
|
2005-09-16 10:18:55 +00:00
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
int sc_establish_context(sc_context_t **ctx_out, const char *app_name)
|
2002-03-24 22:47:35 +00:00
|
|
|
{
|
2006-02-01 22:59:42 +00:00
|
|
|
sc_context_param_t ctx_param;
|
2002-03-24 22:47:35 +00:00
|
|
|
|
2006-02-01 22:59:42 +00:00
|
|
|
memset(&ctx_param, 0, sizeof(sc_context_param_t));
|
|
|
|
ctx_param.ver = 0;
|
|
|
|
ctx_param.app_name = app_name;
|
|
|
|
return sc_context_create(ctx_out, &ctx_param);
|
|
|
|
}
|
|
|
|
|
2012-05-29 17:44:54 +00:00
|
|
|
/* For multithreaded issues */
|
|
|
|
int sc_context_repair(sc_context_t **ctx_out)
|
|
|
|
{
|
|
|
|
/* Must already exist */
|
|
|
|
if ((ctx_out == NULL) || (*ctx_out == NULL) ||
|
|
|
|
((*ctx_out)->app_name == NULL))
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
|
|
|
/* The only thing that should be shared across different contexts are the
|
|
|
|
* card drivers - so rebuild the ATR's
|
|
|
|
*/
|
|
|
|
load_card_atrs(*ctx_out);
|
|
|
|
|
|
|
|
/* TODO: May need to re-open any card driver DLL's */
|
|
|
|
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2006-02-01 22:59:42 +00:00
|
|
|
int sc_context_create(sc_context_t **ctx_out, const sc_context_param_t *parm)
|
|
|
|
{
|
|
|
|
sc_context_t *ctx;
|
|
|
|
struct _sc_ctx_options opts;
|
|
|
|
int r;
|
|
|
|
|
2011-01-22 12:53:09 +00:00
|
|
|
if (ctx_out == NULL || parm == NULL)
|
2006-02-01 22:59:42 +00:00
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
|
|
|
ctx = calloc(1, sizeof(sc_context_t));
|
2002-03-24 22:47:35 +00:00
|
|
|
if (ctx == NULL)
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
memset(&opts, 0, sizeof(opts));
|
2006-02-01 22:59:42 +00:00
|
|
|
|
|
|
|
/* set the application name if set in the parameter options */
|
2011-01-22 12:53:09 +00:00
|
|
|
if (parm->app_name != NULL)
|
2006-02-01 22:59:42 +00:00
|
|
|
ctx->app_name = strdup(parm->app_name);
|
|
|
|
else
|
|
|
|
ctx->app_name = strdup("default");
|
2005-01-03 17:20:17 +00:00
|
|
|
if (ctx->app_name == NULL) {
|
|
|
|
sc_release_context(ctx);
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
2012-02-17 09:06:57 +00:00
|
|
|
|
2015-07-22 15:25:35 +00:00
|
|
|
ctx->flags = parm->flags;
|
2010-06-16 14:12:27 +00:00
|
|
|
set_defaults(ctx, &opts);
|
2015-07-22 15:25:35 +00:00
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
list_init(&ctx->readers);
|
|
|
|
list_attributes_seeker(&ctx->readers, reader_list_seeker);
|
2006-02-01 22:59:42 +00:00
|
|
|
/* set thread context and create mutex object (if specified) */
|
2011-01-22 12:53:09 +00:00
|
|
|
if (parm->thread_ctx != NULL)
|
2006-02-01 22:59:42 +00:00
|
|
|
ctx->thread_ctx = parm->thread_ctx;
|
|
|
|
r = sc_mutex_create(ctx, &ctx->mutex);
|
|
|
|
if (r != SC_SUCCESS) {
|
|
|
|
sc_release_context(ctx);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2002-03-26 11:38:40 +00:00
|
|
|
process_config_file(ctx, &opts);
|
2012-06-11 17:45:26 +00:00
|
|
|
sc_log(ctx, "==================================="); /* first thing in the log */
|
|
|
|
sc_log(ctx, "opensc version: %s", sc_get_version());
|
2005-09-01 14:01:58 +00:00
|
|
|
|
2010-09-11 13:00:47 +00:00
|
|
|
#ifdef ENABLE_PCSC
|
|
|
|
ctx->reader_driver = sc_get_pcsc_driver();
|
2011-04-12 07:40:12 +00:00
|
|
|
/* XXX: remove cardmod pseudoreader driver */
|
|
|
|
#ifdef ENABLE_MINIDRIVER
|
2012-11-11 21:17:17 +00:00
|
|
|
if(strcmp(ctx->app_name, "cardmod") == 0)
|
2010-10-15 08:07:34 +00:00
|
|
|
ctx->reader_driver = sc_get_cardmod_driver();
|
2011-04-12 07:40:12 +00:00
|
|
|
#endif
|
2011-06-23 15:07:26 +00:00
|
|
|
#elif defined(ENABLE_CTAPI)
|
2010-09-11 13:00:47 +00:00
|
|
|
ctx->reader_driver = sc_get_ctapi_driver();
|
2011-06-23 15:07:26 +00:00
|
|
|
#elif defined(ENABLE_OPENCT)
|
2010-09-11 13:00:47 +00:00
|
|
|
ctx->reader_driver = sc_get_openct_driver();
|
|
|
|
#endif
|
|
|
|
|
2012-11-11 21:17:17 +00:00
|
|
|
r = ctx->reader_driver->ops->init(ctx);
|
|
|
|
if (r != SC_SUCCESS) {
|
|
|
|
sc_release_context(ctx);
|
|
|
|
return r;
|
|
|
|
}
|
2012-02-17 09:06:57 +00:00
|
|
|
|
2002-03-24 22:47:35 +00:00
|
|
|
load_card_drivers(ctx, &opts);
|
2010-09-11 13:01:00 +00:00
|
|
|
load_card_atrs(ctx);
|
2016-11-20 18:38:59 +00:00
|
|
|
|
|
|
|
if (!opts.forced_card_driver) {
|
|
|
|
char *driver = getenv("OPENSC_DRIVER");
|
|
|
|
if(driver) {
|
|
|
|
opts.forced_card_driver = strdup(driver);
|
|
|
|
}
|
|
|
|
}
|
2002-04-19 20:07:56 +00:00
|
|
|
if (opts.forced_card_driver) {
|
2006-04-26 10:05:21 +00:00
|
|
|
/* FIXME: check return value? */
|
2002-04-19 20:07:56 +00:00
|
|
|
sc_set_card_driver(ctx, opts.forced_card_driver);
|
|
|
|
free(opts.forced_card_driver);
|
2005-02-06 19:40:40 +00:00
|
|
|
}
|
2010-09-11 13:00:47 +00:00
|
|
|
del_drvs(&opts);
|
2008-04-29 17:01:19 +00:00
|
|
|
sc_ctx_detect_readers(ctx);
|
2002-03-24 22:47:35 +00:00
|
|
|
*ctx_out = ctx;
|
2012-11-11 21:17:17 +00:00
|
|
|
|
2005-02-20 08:26:27 +00:00
|
|
|
return SC_SUCCESS;
|
2002-03-24 22:47:35 +00:00
|
|
|
}
|
|
|
|
|
2011-04-12 07:40:12 +00:00
|
|
|
/* Used by minidriver to pass in provided handles to reader-pcsc */
|
|
|
|
int sc_ctx_use_reader(sc_context_t *ctx, void *pcsc_context_handle, void *pcsc_card_handle)
|
2011-02-09 14:33:52 +00:00
|
|
|
{
|
|
|
|
SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL);
|
|
|
|
if (ctx->reader_driver->ops->use_reader != NULL)
|
|
|
|
return ctx->reader_driver->ops->use_reader(ctx, pcsc_context_handle, pcsc_card_handle);
|
|
|
|
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
/* Following two are only implemented with internal PC/SC and don't consume a reader object */
|
|
|
|
int sc_cancel(sc_context_t *ctx)
|
|
|
|
{
|
2010-09-11 13:00:47 +00:00
|
|
|
SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL);
|
|
|
|
if (ctx->reader_driver->ops->cancel != NULL)
|
|
|
|
return ctx->reader_driver->ops->cancel(ctx);
|
|
|
|
|
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
2010-01-24 15:25:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-09-11 13:00:47 +00:00
|
|
|
int sc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_reader_t **event_reader, unsigned int *event, int timeout, void **reader_states)
|
2010-01-24 15:25:08 +00:00
|
|
|
{
|
2010-09-11 13:00:47 +00:00
|
|
|
SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL);
|
2010-09-11 13:01:06 +00:00
|
|
|
if (ctx->reader_driver->ops->wait_for_event != NULL)
|
2010-09-11 13:00:47 +00:00
|
|
|
return ctx->reader_driver->ops->wait_for_event(ctx, event_mask, event_reader, event, timeout, reader_states);
|
2012-02-17 09:06:57 +00:00
|
|
|
|
2010-09-11 13:00:47 +00:00
|
|
|
return SC_ERROR_NOT_SUPPORTED;
|
2010-01-24 15:25:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
int sc_release_context(sc_context_t *ctx)
|
2002-03-24 22:47:35 +00:00
|
|
|
{
|
2010-01-24 15:25:08 +00:00
|
|
|
unsigned int i;
|
2002-03-24 22:47:35 +00:00
|
|
|
|
|
|
|
assert(ctx != NULL);
|
2010-03-15 12:17:13 +00:00
|
|
|
SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);
|
2011-02-03 21:18:26 +00:00
|
|
|
while (list_size(&ctx->readers)) {
|
|
|
|
sc_reader_t *rdr = (sc_reader_t *) list_get_at(&ctx->readers, 0);
|
|
|
|
_sc_delete_reader(ctx, rdr);
|
2002-03-24 22:47:35 +00:00
|
|
|
}
|
2010-01-24 15:25:08 +00:00
|
|
|
|
2010-09-11 13:00:47 +00:00
|
|
|
if (ctx->reader_driver->ops->finish != NULL)
|
|
|
|
ctx->reader_driver->ops->finish(ctx);
|
2005-02-06 19:40:40 +00:00
|
|
|
|
2004-10-18 08:24:12 +00:00
|
|
|
for (i = 0; ctx->card_drivers[i]; i++) {
|
|
|
|
struct sc_card_driver *drv = ctx->card_drivers[i];
|
2006-02-23 13:40:03 +00:00
|
|
|
|
2005-02-14 09:12:44 +00:00
|
|
|
if (drv->atr_map)
|
|
|
|
_sc_free_atr(ctx, drv);
|
2006-02-23 13:40:03 +00:00
|
|
|
if (drv->dll)
|
2011-02-16 19:02:11 +00:00
|
|
|
sc_dlclose(drv->dll);
|
2002-03-24 22:47:35 +00:00
|
|
|
}
|
2006-02-01 22:59:42 +00:00
|
|
|
if (ctx->preferred_language != NULL)
|
2003-10-22 18:16:21 +00:00
|
|
|
free(ctx->preferred_language);
|
2006-02-05 19:35:55 +00:00
|
|
|
if (ctx->mutex != NULL) {
|
|
|
|
int r = sc_mutex_destroy(ctx, ctx->mutex);
|
|
|
|
if (r != SC_SUCCESS) {
|
2012-06-11 17:45:26 +00:00
|
|
|
sc_log(ctx, "unable to destroy mutex");
|
2006-02-05 19:35:55 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
2006-02-01 22:59:42 +00:00
|
|
|
if (ctx->conf != NULL)
|
2002-03-26 20:56:13 +00:00
|
|
|
scconf_free(ctx->conf);
|
2009-09-14 08:46:59 +00:00
|
|
|
if (ctx->debug_file && (ctx->debug_file != stdout && ctx->debug_file != stderr))
|
2006-02-05 19:35:55 +00:00
|
|
|
fclose(ctx->debug_file);
|
2012-05-29 17:44:54 +00:00
|
|
|
if (ctx->debug_filename != NULL)
|
|
|
|
free(ctx->debug_filename);
|
2006-02-01 22:59:42 +00:00
|
|
|
if (ctx->app_name != NULL)
|
|
|
|
free(ctx->app_name);
|
2012-05-29 17:44:54 +00:00
|
|
|
list_destroy(&ctx->readers);
|
2005-09-17 10:44:45 +00:00
|
|
|
sc_mem_clear(ctx, sizeof(*ctx));
|
2002-03-24 22:47:35 +00:00
|
|
|
free(ctx);
|
2005-02-20 08:26:27 +00:00
|
|
|
return SC_SUCCESS;
|
2002-03-24 22:47:35 +00:00
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
int sc_set_card_driver(sc_context_t *ctx, const char *short_name)
|
2002-03-24 22:47:35 +00:00
|
|
|
{
|
|
|
|
int i = 0, match = 0;
|
2005-02-06 19:40:40 +00:00
|
|
|
|
2006-02-01 22:59:42 +00:00
|
|
|
sc_mutex_lock(ctx, ctx->mutex);
|
2002-03-24 22:47:35 +00:00
|
|
|
if (short_name == NULL) {
|
|
|
|
ctx->forced_driver = NULL;
|
|
|
|
match = 1;
|
2015-01-28 04:59:41 +00:00
|
|
|
} else while (i < SC_MAX_CARD_DRIVERS && ctx->card_drivers[i] != NULL) {
|
2002-12-19 16:16:42 +00:00
|
|
|
struct sc_card_driver *drv = ctx->card_drivers[i];
|
2002-03-24 22:47:35 +00:00
|
|
|
|
|
|
|
if (strcmp(short_name, drv->short_name) == 0) {
|
|
|
|
ctx->forced_driver = drv;
|
|
|
|
match = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
2006-02-01 22:59:42 +00:00
|
|
|
sc_mutex_unlock(ctx, ctx->mutex);
|
2002-03-24 22:47:35 +00:00
|
|
|
if (match == 0)
|
|
|
|
return SC_ERROR_OBJECT_NOT_FOUND; /* FIXME: invent error */
|
2005-02-20 08:26:27 +00:00
|
|
|
return SC_SUCCESS;
|
2002-03-24 22:47:35 +00:00
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
int sc_get_cache_dir(sc_context_t *ctx, char *buf, size_t bufsize)
|
2002-03-24 22:47:35 +00:00
|
|
|
{
|
|
|
|
char *homedir;
|
2002-11-27 14:27:53 +00:00
|
|
|
const char *cache_dir;
|
2015-02-05 18:18:05 +00:00
|
|
|
scconf_block *conf_block = NULL;
|
2003-04-23 11:46:07 +00:00
|
|
|
#ifdef _WIN32
|
2005-03-23 23:10:50 +00:00
|
|
|
char temp_path[PATH_MAX];
|
2003-04-23 11:46:07 +00:00
|
|
|
#endif
|
2015-02-05 18:18:05 +00:00
|
|
|
conf_block = sc_get_conf_block(ctx, "framework", "pkcs15", 1);
|
|
|
|
cache_dir = scconf_get_str(conf_block, "file_cache_dir", NULL);
|
|
|
|
if (cache_dir != NULL) {
|
|
|
|
if (bufsize <= strlen(cache_dir))
|
|
|
|
return SC_ERROR_BUFFER_TOO_SMALL;
|
|
|
|
strcpy(buf, cache_dir);
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
2002-03-24 22:47:35 +00:00
|
|
|
|
2002-11-27 14:27:53 +00:00
|
|
|
#ifndef _WIN32
|
|
|
|
cache_dir = ".eid/cache";
|
2002-03-24 22:47:35 +00:00
|
|
|
homedir = getenv("HOME");
|
2002-11-27 14:27:53 +00:00
|
|
|
#else
|
|
|
|
cache_dir = "eid-cache";
|
|
|
|
homedir = getenv("USERPROFILE");
|
2003-04-23 11:46:07 +00:00
|
|
|
/* If USERPROFILE isn't defined, assume it's a single-user OS
|
|
|
|
* and put the cache dir in the Windows dir (usually C:\\WINDOWS) */
|
|
|
|
if (homedir == NULL || homedir[0] == '\0') {
|
2015-03-28 09:00:47 +00:00
|
|
|
GetWindowsDirectoryA(temp_path, sizeof(temp_path));
|
2005-03-23 23:10:50 +00:00
|
|
|
homedir = temp_path;
|
2003-04-23 11:46:07 +00:00
|
|
|
}
|
2002-11-27 14:27:53 +00:00
|
|
|
#endif
|
2002-03-24 22:47:35 +00:00
|
|
|
if (homedir == NULL)
|
|
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
if (snprintf(buf, bufsize, "%s/%s", homedir, cache_dir) < 0)
|
|
|
|
return SC_ERROR_BUFFER_TOO_SMALL;
|
2005-02-20 08:26:27 +00:00
|
|
|
return SC_SUCCESS;
|
2002-03-24 22:47:35 +00:00
|
|
|
}
|
2003-01-09 09:18:02 +00:00
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
int sc_make_cache_dir(sc_context_t *ctx)
|
2003-01-09 09:18:02 +00:00
|
|
|
{
|
|
|
|
char dirname[PATH_MAX], *sp;
|
2015-11-28 14:33:38 +00:00
|
|
|
int r, mkdir_checker;
|
2004-10-29 20:08:54 +00:00
|
|
|
size_t j, namelen;
|
2003-01-09 09:18:02 +00:00
|
|
|
|
|
|
|
if ((r = sc_get_cache_dir(ctx, dirname, sizeof(dirname))) < 0)
|
|
|
|
return r;
|
|
|
|
namelen = strlen(dirname);
|
|
|
|
|
|
|
|
while (1) {
|
2008-03-06 16:06:59 +00:00
|
|
|
#ifdef _WIN32
|
2015-11-28 14:33:38 +00:00
|
|
|
mkdir_checker = mkdir(dirname) >= 0;
|
2008-03-06 16:06:59 +00:00
|
|
|
#else
|
2015-11-28 14:33:38 +00:00
|
|
|
mkdir_checker = mkdir(dirname, 0700) >= 0;
|
2008-03-06 16:06:59 +00:00
|
|
|
#endif
|
2015-11-28 14:33:38 +00:00
|
|
|
if (mkdir_checker)
|
2003-01-09 09:18:02 +00:00
|
|
|
break;
|
2012-06-11 17:45:26 +00:00
|
|
|
|
|
|
|
if (errno != ENOENT || (sp = strrchr(dirname, '/')) == NULL
|
|
|
|
|| sp == dirname)
|
2003-01-09 09:18:02 +00:00
|
|
|
goto failed;
|
|
|
|
*sp = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We may have stripped one or more path components from
|
|
|
|
* the directory name. Restore them */
|
|
|
|
while (1) {
|
|
|
|
j = strlen(dirname);
|
|
|
|
if (j >= namelen)
|
|
|
|
break;
|
|
|
|
dirname[j] = '/';
|
2008-03-06 16:06:59 +00:00
|
|
|
#ifdef _WIN32
|
2015-11-28 14:33:38 +00:00
|
|
|
mkdir_checker = mkdir(dirname) < 0;
|
2008-03-06 16:06:59 +00:00
|
|
|
#else
|
2015-11-28 14:33:38 +00:00
|
|
|
mkdir_checker = mkdir(dirname, 0700) < 0;
|
2008-03-06 16:06:59 +00:00
|
|
|
#endif
|
2015-11-28 14:33:38 +00:00
|
|
|
if (mkdir_checker)
|
2003-01-09 09:18:02 +00:00
|
|
|
goto failed;
|
|
|
|
}
|
2005-02-20 08:26:27 +00:00
|
|
|
return SC_SUCCESS;
|
2003-01-09 09:18:02 +00:00
|
|
|
|
|
|
|
/* for lack of a better return code */
|
2012-06-11 17:45:26 +00:00
|
|
|
failed:
|
|
|
|
sc_log(ctx, "failed to create cache directory");
|
2003-01-09 09:18:02 +00:00
|
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
}
|