2002-04-02 13:26:42 +00:00
|
|
|
/*
|
|
|
|
* Initialize Cards according to PKCS#15
|
|
|
|
*
|
2015-10-05 12:06:23 +00:00
|
|
|
* Copyright (C) 2002 Olaf Kirch <okir@suse.de>
|
2002-04-02 13:26:42 +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
|
2003-10-13 16:13:12 +00:00
|
|
|
*
|
|
|
|
* Random notes
|
|
|
|
* - the "key" command should go away, it's obsolete
|
2002-04-02 13:26:42 +00:00
|
|
|
*/
|
|
|
|
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <string.h>
|
2003-06-25 10:19:08 +00:00
|
|
|
#include <limits.h>
|
2002-10-19 14:04:52 +00:00
|
|
|
#ifdef HAVE_STRINGS_H
|
2002-05-20 09:19:41 +00:00
|
|
|
#include <strings.h>
|
2002-10-19 14:04:52 +00:00
|
|
|
#endif
|
|
|
|
#ifdef HAVE_UNISTD_H
|
2002-04-02 13:26:42 +00:00
|
|
|
#include <unistd.h>
|
2002-10-19 14:04:52 +00:00
|
|
|
#endif
|
2002-04-02 13:26:42 +00:00
|
|
|
#include <assert.h>
|
2002-04-15 13:42:10 +00:00
|
|
|
#include <stdlib.h>
|
2010-03-04 08:14:36 +00:00
|
|
|
|
2012-04-02 21:40:05 +00:00
|
|
|
#ifdef _WIN32
|
2010-08-25 12:51:55 +00:00
|
|
|
#include <windows.h>
|
|
|
|
#include <winreg.h>
|
|
|
|
#endif
|
|
|
|
|
2010-03-04 08:14:36 +00:00
|
|
|
#include "common/compat_strlcpy.h"
|
|
|
|
#include "scconf/scconf.h"
|
|
|
|
#include "libopensc/log.h"
|
|
|
|
#include "libopensc/pkcs15.h"
|
2002-04-02 13:26:42 +00:00
|
|
|
#include "pkcs15-init.h"
|
|
|
|
#include "profile.h"
|
|
|
|
|
|
|
|
#define DEF_PRKEY_RSA_ACCESS 0x1D
|
|
|
|
#define DEF_PRKEY_DSA_ACCESS 0x12
|
|
|
|
#define DEF_PUBKEY_ACCESS 0x12
|
|
|
|
|
2010-01-16 20:55:45 +00:00
|
|
|
#define TEMPLATE_FILEID_MIN_DIFF 0x20
|
|
|
|
|
|
|
|
/*
|
2012-04-02 21:40:05 +00:00
|
|
|
#define DEBUG_PROFILE
|
2010-01-16 20:55:45 +00:00
|
|
|
*/
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
/*
|
|
|
|
* Parser state
|
|
|
|
*/
|
|
|
|
struct state {
|
|
|
|
struct state * frame;
|
|
|
|
const char * filename;
|
|
|
|
struct sc_profile * profile;
|
|
|
|
struct file_info * file;
|
|
|
|
struct pin_info * pin;
|
|
|
|
struct auth_info * key;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
struct command {
|
|
|
|
const char * name;
|
|
|
|
int min_args, max_args;
|
2002-04-03 11:52:30 +00:00
|
|
|
int (*func)(struct state *, int, char **);
|
2002-04-02 13:26:42 +00:00
|
|
|
};
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
struct block {
|
|
|
|
const char * name;
|
|
|
|
int (*handler)(struct state *,
|
|
|
|
struct block *,
|
|
|
|
const char *,
|
|
|
|
scconf_block *);
|
|
|
|
struct command * cmd_info;
|
|
|
|
struct block * blk_info;
|
2002-04-02 13:26:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct map {
|
|
|
|
const char * name;
|
|
|
|
unsigned int val;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct map aclNames[] = {
|
|
|
|
{ "NONE", SC_AC_NONE },
|
|
|
|
{ "NEVER", SC_AC_NEVER },
|
|
|
|
{ "CHV", SC_AC_CHV },
|
|
|
|
{ "TERM", SC_AC_TERM },
|
|
|
|
{ "PRO", SC_AC_PRO },
|
|
|
|
{ "AUT", SC_AC_AUT },
|
|
|
|
{ "KEY", SC_AC_AUT },
|
2011-01-17 14:03:01 +00:00
|
|
|
{ "SEN", SC_AC_SEN },
|
|
|
|
{ "IDA", SC_AC_IDA },
|
|
|
|
{ "SCB", SC_AC_SCB },
|
2007-06-21 11:07:00 +00:00
|
|
|
{ NULL, 0 }
|
2002-04-02 13:26:42 +00:00
|
|
|
};
|
|
|
|
static struct map fileOpNames[] = {
|
|
|
|
{ "SELECT", SC_AC_OP_SELECT },
|
|
|
|
{ "LOCK", SC_AC_OP_LOCK },
|
|
|
|
{ "DELETE", SC_AC_OP_DELETE },
|
2013-02-24 18:32:50 +00:00
|
|
|
{ "DELETE-SELF",SC_AC_OP_DELETE_SELF },
|
2002-04-02 13:26:42 +00:00
|
|
|
{ "CREATE", SC_AC_OP_CREATE },
|
2013-07-08 17:08:56 +00:00
|
|
|
{ "CREATE-EF", SC_AC_OP_CREATE_EF },
|
|
|
|
{ "CREATE-DF", SC_AC_OP_CREATE_DF },
|
2002-04-02 13:26:42 +00:00
|
|
|
{ "REHABILITATE",SC_AC_OP_REHABILITATE },
|
|
|
|
{ "INVALIDATE", SC_AC_OP_INVALIDATE },
|
|
|
|
{ "FILES", SC_AC_OP_LIST_FILES },
|
|
|
|
{ "READ", SC_AC_OP_READ },
|
|
|
|
{ "UPDATE", SC_AC_OP_UPDATE },
|
|
|
|
{ "WRITE", SC_AC_OP_WRITE },
|
|
|
|
{ "ERASE", SC_AC_OP_ERASE },
|
2009-11-13 09:45:21 +00:00
|
|
|
{ "CRYPTO", SC_AC_OP_CRYPTO },
|
|
|
|
{ "PIN-DEFINE", SC_AC_OP_PIN_DEFINE },
|
|
|
|
{ "PIN-CHANGE", SC_AC_OP_PIN_CHANGE },
|
|
|
|
{ "PIN-RESET", SC_AC_OP_PIN_RESET },
|
2013-07-08 17:08:56 +00:00
|
|
|
{ "PIN-USE", SC_AC_OP_PIN_USE },
|
2010-09-04 20:46:07 +00:00
|
|
|
{ "GENERATE", SC_AC_OP_GENERATE },
|
2011-01-02 14:02:47 +00:00
|
|
|
{ "PSO-COMPUTE-SIGNATURE", SC_AC_OP_PSO_COMPUTE_SIGNATURE },
|
|
|
|
{ "INTERNAL-AUTHENTICATE", SC_AC_OP_INTERNAL_AUTHENTICATE },
|
|
|
|
{ "PSO-DECRYPT", SC_AC_OP_PSO_DECRYPT },
|
|
|
|
{ "RESIZE", SC_AC_OP_RESIZE },
|
2013-02-24 18:32:50 +00:00
|
|
|
{ "ADMIN", SC_AC_OP_ADMIN },
|
2013-07-08 17:08:56 +00:00
|
|
|
{ "ACTIVATE", SC_AC_OP_ACTIVATE },
|
|
|
|
{ "DEACTIVATE", SC_AC_OP_DEACTIVATE },
|
2007-06-21 11:07:00 +00:00
|
|
|
{ NULL, 0 }
|
2002-04-02 13:26:42 +00:00
|
|
|
};
|
2002-04-05 08:45:14 +00:00
|
|
|
static struct map fileTypeNames[] = {
|
|
|
|
{ "EF", SC_FILE_TYPE_WORKING_EF },
|
|
|
|
{ "INTERNAL-EF",SC_FILE_TYPE_INTERNAL_EF },
|
|
|
|
{ "DF", SC_FILE_TYPE_DF },
|
2010-01-21 09:41:40 +00:00
|
|
|
{ "BSO", SC_FILE_TYPE_BSO },
|
2007-06-21 11:07:00 +00:00
|
|
|
{ NULL, 0 }
|
2002-04-05 08:45:14 +00:00
|
|
|
};
|
|
|
|
static struct map fileStructureNames[] = {
|
2002-04-02 13:26:42 +00:00
|
|
|
{ "TRANSPARENT", SC_FILE_EF_TRANSPARENT },
|
|
|
|
{ "LINEAR-FIXED", SC_FILE_EF_LINEAR_FIXED },
|
|
|
|
{ "LINEAR-FIXED-TLV", SC_FILE_EF_LINEAR_FIXED_TLV },
|
|
|
|
{ "LINEAR-VARIABLE", SC_FILE_EF_LINEAR_VARIABLE },
|
|
|
|
{ "LINEAR-VARIABLE-TLV",SC_FILE_EF_LINEAR_VARIABLE_TLV },
|
|
|
|
{ "CYCLIC", SC_FILE_EF_CYCLIC },
|
|
|
|
{ "CYCLIC-TLV", SC_FILE_EF_CYCLIC_TLV },
|
2007-06-21 11:07:00 +00:00
|
|
|
{ NULL, 0 }
|
2002-04-02 13:26:42 +00:00
|
|
|
};
|
|
|
|
static struct map pkcs15DfNames[] = {
|
|
|
|
{ "PRKDF", SC_PKCS15_PRKDF },
|
|
|
|
{ "PUKDF", SC_PKCS15_PUKDF },
|
|
|
|
{ "PUKDF-TRUSTED", SC_PKCS15_PUKDF_TRUSTED },
|
|
|
|
{ "SKDF", SC_PKCS15_SKDF },
|
|
|
|
{ "CDF", SC_PKCS15_CDF },
|
|
|
|
{ "CDF-TRUSTED", SC_PKCS15_CDF_TRUSTED },
|
|
|
|
{ "CDF-USEFUL", SC_PKCS15_CDF_USEFUL },
|
|
|
|
{ "DODF", SC_PKCS15_DODF },
|
|
|
|
{ "AODF", SC_PKCS15_AODF },
|
2007-06-21 11:07:00 +00:00
|
|
|
{ NULL, 0 }
|
2002-04-02 13:26:42 +00:00
|
|
|
};
|
|
|
|
static struct map pinTypeNames[] = {
|
2010-02-20 23:14:45 +00:00
|
|
|
{ "BCD", SC_PKCS15_PIN_TYPE_BCD },
|
|
|
|
{ "ascii-numeric", SC_PKCS15_PIN_TYPE_ASCII_NUMERIC },
|
|
|
|
{ "utf8", SC_PKCS15_PIN_TYPE_UTF8 },
|
|
|
|
{ "half-nibble-bcd", SC_PKCS15_PIN_TYPE_HALFNIBBLE_BCD },
|
|
|
|
{ "iso9564-1", SC_PKCS15_PIN_TYPE_ISO9564_1 },
|
2007-06-21 11:07:00 +00:00
|
|
|
{ NULL, 0 }
|
2002-04-02 13:26:42 +00:00
|
|
|
};
|
2002-04-03 11:52:30 +00:00
|
|
|
static struct map pinIdNames[] = {
|
|
|
|
{ "pin", SC_PKCS15INIT_USER_PIN },
|
|
|
|
{ "puk", SC_PKCS15INIT_USER_PUK },
|
|
|
|
{ "user-pin", SC_PKCS15INIT_USER_PIN },
|
|
|
|
{ "user-puk", SC_PKCS15INIT_USER_PUK },
|
|
|
|
{ "sopin", SC_PKCS15INIT_SO_PIN },
|
|
|
|
{ "sopuk", SC_PKCS15INIT_SO_PUK },
|
|
|
|
{ "so-pin", SC_PKCS15INIT_SO_PIN },
|
|
|
|
{ "so-puk", SC_PKCS15INIT_SO_PUK },
|
2007-06-21 11:07:00 +00:00
|
|
|
{ NULL, 0 }
|
2002-04-02 13:26:42 +00:00
|
|
|
};
|
2002-04-08 09:29:15 +00:00
|
|
|
static struct map pinFlagNames[] = {
|
2010-01-27 18:18:10 +00:00
|
|
|
{ "case-sensitive", SC_PKCS15_PIN_FLAG_CASE_SENSITIVE },
|
|
|
|
{ "local", SC_PKCS15_PIN_FLAG_LOCAL },
|
|
|
|
{ "change-disabled", SC_PKCS15_PIN_FLAG_CHANGE_DISABLED },
|
|
|
|
{ "unblock-disabled", SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED },
|
|
|
|
{ "initialized", SC_PKCS15_PIN_FLAG_INITIALIZED },
|
|
|
|
{ "needs-padding", SC_PKCS15_PIN_FLAG_NEEDS_PADDING },
|
|
|
|
{ "unblockingPin", SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN },
|
|
|
|
{ "soPin", SC_PKCS15_PIN_FLAG_SO_PIN },
|
|
|
|
{ "disable-allowed", SC_PKCS15_PIN_FLAG_DISABLE_ALLOW },
|
|
|
|
{ "integrity-protected", SC_PKCS15_PIN_FLAG_INTEGRITY_PROTECTED },
|
|
|
|
{ "confidentiality-protected", SC_PKCS15_PIN_FLAG_CONFIDENTIALITY_PROTECTED },
|
|
|
|
{ "exchangeRefData", SC_PKCS15_PIN_FLAG_EXCHANGE_REF_DATA },
|
2007-06-21 11:07:00 +00:00
|
|
|
{ NULL, 0 }
|
2002-04-08 09:29:15 +00:00
|
|
|
};
|
2009-11-13 09:45:21 +00:00
|
|
|
static struct map idStyleNames[] = {
|
|
|
|
{ "native", SC_PKCS15INIT_ID_STYLE_NATIVE },
|
|
|
|
{ "mozilla", SC_PKCS15INIT_ID_STYLE_MOZILLA },
|
|
|
|
{ "rfc2459", SC_PKCS15INIT_ID_STYLE_RFC2459 },
|
|
|
|
{ NULL, 0 }
|
|
|
|
};
|
2012-05-26 07:17:21 +00:00
|
|
|
static struct map mdStyleNames[] = {
|
|
|
|
{ "none", SC_PKCS15INIT_MD_STYLE_NONE },
|
|
|
|
{ "gemalto", SC_PKCS15INIT_MD_STYLE_GEMALTO },
|
|
|
|
{ NULL, 0 }
|
|
|
|
};
|
2002-04-05 08:45:14 +00:00
|
|
|
static struct {
|
|
|
|
const char * name;
|
|
|
|
struct map * addr;
|
|
|
|
} mapNames[] = {
|
|
|
|
{ "file ACL", aclNames },
|
|
|
|
{ "file operation", fileOpNames },
|
|
|
|
{ "file type", fileTypeNames },
|
|
|
|
{ "file structure", fileStructureNames},
|
|
|
|
{ "PKCS#15 file name", pkcs15DfNames },
|
|
|
|
{ "pin encoding", pinTypeNames },
|
|
|
|
{ "pin name", pinIdNames },
|
2002-04-08 09:29:15 +00:00
|
|
|
{ "pin flag", pinFlagNames },
|
2002-04-05 08:45:14 +00:00
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2003-11-19 20:37:02 +00:00
|
|
|
static int process_conf(struct sc_profile *, scconf_context *);
|
2002-04-03 11:52:30 +00:00
|
|
|
static int process_block(struct state *, struct block *,
|
|
|
|
const char *, scconf_block *);
|
|
|
|
static void init_state(struct state *, struct state *);
|
|
|
|
static int get_authid(struct state *, const char *,
|
|
|
|
unsigned int *, unsigned int *);
|
|
|
|
static int get_uint(struct state *, const char *, unsigned int *);
|
2003-10-13 16:13:12 +00:00
|
|
|
static int get_bool(struct state *, const char *, unsigned int *);
|
|
|
|
static int get_uint_eval(struct state *, int, char **,
|
|
|
|
unsigned int *);
|
2002-04-03 11:52:30 +00:00
|
|
|
static int map_str2int(struct state *, const char *,
|
|
|
|
unsigned int *, struct map *);
|
|
|
|
static int setstr(char **strp, const char *value);
|
|
|
|
static void parse_error(struct state *, const char *, ...);
|
|
|
|
|
2011-03-23 15:12:40 +00:00
|
|
|
static struct file_info * sc_profile_instantiate_file(sc_profile_t *,
|
2003-10-13 16:13:12 +00:00
|
|
|
struct file_info *, struct file_info *,
|
|
|
|
unsigned int);
|
2011-03-23 15:12:40 +00:00
|
|
|
static struct file_info * sc_profile_find_file(struct sc_profile *,
|
2003-10-13 16:13:12 +00:00
|
|
|
const sc_path_t *, const char *);
|
2011-03-23 15:12:40 +00:00
|
|
|
static struct file_info * sc_profile_find_file_by_path(
|
2002-04-03 11:52:30 +00:00
|
|
|
struct sc_profile *,
|
2005-03-09 00:04:44 +00:00
|
|
|
const sc_path_t *);
|
2002-04-03 11:52:30 +00:00
|
|
|
|
2011-03-23 15:10:10 +00:00
|
|
|
static struct pin_info * new_pin(struct sc_profile *, int);
|
2011-03-23 15:12:40 +00:00
|
|
|
static struct file_info * new_file(struct state *, const char *,
|
2002-04-03 11:52:30 +00:00
|
|
|
unsigned int);
|
2011-03-23 15:12:40 +00:00
|
|
|
static struct file_info * add_file(sc_profile_t *, const char *,
|
|
|
|
sc_file_t *, struct file_info *);
|
2003-10-13 16:13:12 +00:00
|
|
|
static void free_file_list(struct file_info **);
|
|
|
|
static void append_file(sc_profile_t *, struct file_info *);
|
2011-03-23 15:10:10 +00:00
|
|
|
static struct auth_info * new_key(struct sc_profile *,
|
2002-04-03 11:52:30 +00:00
|
|
|
unsigned int, unsigned int);
|
2003-04-11 10:32:15 +00:00
|
|
|
static void set_pin_defaults(struct sc_profile *,
|
|
|
|
struct pin_info *);
|
2003-10-13 16:13:12 +00:00
|
|
|
static void new_macro(sc_profile_t *, const char *, scconf_list *);
|
|
|
|
static sc_macro_t * find_macro(sc_profile_t *, const char *);
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2005-03-09 00:04:44 +00:00
|
|
|
static sc_file_t *
|
2002-04-02 13:26:42 +00:00
|
|
|
init_file(unsigned int type)
|
|
|
|
{
|
|
|
|
struct sc_file *file;
|
|
|
|
unsigned int op;
|
|
|
|
|
|
|
|
file = sc_file_new();
|
|
|
|
for (op = 0; op < SC_MAX_AC_OPS; op++) {
|
|
|
|
sc_file_add_acl_entry(file, op, SC_AC_NONE, 0);
|
|
|
|
}
|
|
|
|
file->type = type;
|
|
|
|
file->status = SC_FILE_STATUS_ACTIVATED;
|
2010-01-21 09:41:40 +00:00
|
|
|
if (file->type != SC_FILE_TYPE_DF && file->type != SC_FILE_TYPE_BSO)
|
2005-07-11 21:28:55 +00:00
|
|
|
file->ef_structure = SC_FILE_EF_TRANSPARENT;
|
2002-04-02 13:26:42 +00:00
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize profile
|
|
|
|
*/
|
2002-04-05 10:05:50 +00:00
|
|
|
struct sc_profile *
|
2005-03-07 14:00:31 +00:00
|
|
|
sc_profile_new(void)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
|
|
|
struct sc_pkcs15_card *p15card;
|
2002-04-05 10:05:50 +00:00
|
|
|
struct sc_profile *pro;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
Do not cast the return value of malloc(3) and calloc(3)
From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety
" Casting and type safety
malloc returns a void pointer (void *), which indicates that it is a
pointer to a region of unknown data type. One may "cast" (see type
conversion) this pointer to a specific type, as in
int *ptr = (int*)malloc(10 * sizeof (int));
When using C, this is considered bad practice; it is redundant under the
C standard. Moreover, putting in a cast may mask failure to include the
header stdlib.h, in which the prototype for malloc is found. In the
absence of a prototype for malloc, the C compiler will assume that
malloc returns an int, and will issue a warning in a context such as the
above, provided the error is not masked by a cast. On certain
architectures and data models (such as LP64 on 64 bit systems, where
long and pointers are 64 bit and int is 32 bit), this error can actually
result in undefined behavior, as the implicitly declared malloc returns
a 32 bit value whereas the actually defined function returns a 64 bit
value. Depending on calling conventions and memory layout, this may
result in stack smashing.
The returned pointer need not be explicitly cast to a more specific
pointer type, since ANSI C defines an implicit conversion between the
void pointer type and other pointers to objects. An explicit cast of
malloc's return value is sometimes performed because malloc originally
returned a char *, but this cast is unnecessary in standard C
code.[4][5] Omitting the cast, however, creates an incompatibility with
C++, which does require it.
The lack of a specific pointer type returned from malloc is type-unsafe
behaviour: malloc allocates based on byte count but not on type. This
distinguishes it from the C++ new operator that returns a pointer whose
type relies on the operand. (see C Type Safety). "
See also
http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
|
|
|
pro = calloc(1, sizeof(*pro));
|
2005-01-03 17:25:18 +00:00
|
|
|
if (pro == NULL)
|
|
|
|
return NULL;
|
2003-10-21 11:05:35 +00:00
|
|
|
pro->p15_spec = p15card = sc_pkcs15_card_new();
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2005-08-29 20:48:00 +00:00
|
|
|
pro->pkcs15.do_last_update = 1;
|
2005-08-24 09:50:48 +00:00
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
if (p15card) {
|
2010-10-05 15:44:58 +00:00
|
|
|
p15card->tokeninfo->label = strdup("OpenSC Card");
|
|
|
|
p15card->tokeninfo->manufacturer_id = strdup("OpenSC Project");
|
|
|
|
p15card->tokeninfo->serial_number = strdup("0000");
|
|
|
|
p15card->tokeninfo->flags = SC_PKCS15_TOKEN_EID_COMPLIANT;
|
2010-10-05 16:02:57 +00:00
|
|
|
p15card->tokeninfo->version = 0;
|
2005-12-05 21:55:03 +00:00
|
|
|
|
|
|
|
/* Set up EF(TokenInfo) and EF(ODF) */
|
|
|
|
p15card->file_tokeninfo = init_file(SC_FILE_TYPE_WORKING_EF);
|
|
|
|
p15card->file_odf = init_file(SC_FILE_TYPE_WORKING_EF);
|
2005-12-17 19:53:12 +00:00
|
|
|
p15card->file_unusedspace = init_file(SC_FILE_TYPE_WORKING_EF);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Assume card does RSA natively, but no DSA */
|
|
|
|
pro->rsa_access_flags = DEF_PRKEY_RSA_ACCESS;
|
|
|
|
pro->dsa_access_flags = DEF_PRKEY_DSA_ACCESS;
|
2010-02-20 23:14:45 +00:00
|
|
|
pro->pin_encoding = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC;
|
2002-04-02 13:26:42 +00:00
|
|
|
pro->pin_minlen = 4;
|
|
|
|
pro->pin_maxlen = 8;
|
2012-04-02 21:40:05 +00:00
|
|
|
pro->id_style = SC_PKCS15INIT_ID_STYLE_NATIVE;
|
2002-04-05 10:05:50 +00:00
|
|
|
|
|
|
|
return pro;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2003-11-19 20:37:02 +00:00
|
|
|
sc_profile_load(struct sc_profile *profile, const char *filename)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2005-03-23 23:10:50 +00:00
|
|
|
struct sc_context *ctx = profile->card->ctx;
|
2002-04-03 11:52:30 +00:00
|
|
|
scconf_context *conf;
|
2005-03-23 23:10:50 +00:00
|
|
|
const char *profile_dir = NULL;
|
|
|
|
char path[PATH_MAX];
|
2016-04-13 12:27:26 +00:00
|
|
|
int res = 0, i;
|
2010-08-25 12:51:55 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
char temp_path[PATH_MAX];
|
2016-06-05 01:04:20 +00:00
|
|
|
size_t temp_len;
|
2010-08-25 12:51:55 +00:00
|
|
|
#endif
|
2012-04-02 21:40:05 +00:00
|
|
|
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_CALLED(ctx);
|
2005-03-23 23:10:50 +00:00
|
|
|
for (i = 0; ctx->conf_blocks[i]; i++) {
|
|
|
|
profile_dir = scconf_get_str(ctx->conf_blocks[i], "profile_dir", NULL);
|
|
|
|
if (profile_dir)
|
|
|
|
break;
|
|
|
|
}
|
2016-04-13 12:27:26 +00:00
|
|
|
|
2005-03-23 23:10:50 +00:00
|
|
|
if (!profile_dir) {
|
2010-08-25 12:51:55 +00:00
|
|
|
#ifdef _WIN32
|
2018-07-28 14:52:29 +00:00
|
|
|
temp_len = PATH_MAX - 1;
|
2016-04-13 12:27:26 +00:00
|
|
|
res = sc_ctx_win32_get_config_value(NULL, "ProfileDir", "Software\\OpenSC Project\\OpenSC",
|
|
|
|
temp_path, &temp_len);
|
|
|
|
if (res)
|
|
|
|
LOG_FUNC_RETURN(ctx, res);
|
2018-07-28 14:52:29 +00:00
|
|
|
temp_path[temp_len] = '\0';
|
2016-04-13 12:27:26 +00:00
|
|
|
profile_dir = temp_path;
|
2010-08-25 12:51:55 +00:00
|
|
|
#else
|
2009-01-16 21:27:46 +00:00
|
|
|
profile_dir = SC_PKCS15_PROFILE_DIRECTORY;
|
2010-08-25 12:51:55 +00:00
|
|
|
#endif
|
2009-01-16 21:27:46 +00:00
|
|
|
}
|
2011-01-17 16:49:56 +00:00
|
|
|
sc_log(ctx, "Using profile directory '%s'.", profile_dir);
|
2005-03-23 23:10:50 +00:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
2011-01-17 14:03:01 +00:00
|
|
|
snprintf(path, sizeof(path), "%s\\%s.%s", profile_dir, filename, SC_PKCS15_PROFILE_SUFFIX);
|
2005-03-23 23:10:50 +00:00
|
|
|
#else /* _WIN32 */
|
2011-01-17 14:03:01 +00:00
|
|
|
snprintf(path, sizeof(path), "%s/%s.%s", profile_dir, filename, SC_PKCS15_PROFILE_SUFFIX);
|
2005-03-23 23:10:50 +00:00
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
2011-01-17 16:49:56 +00:00
|
|
|
sc_log(ctx, "Trying profile file %s", path);
|
2005-03-23 23:10:50 +00:00
|
|
|
|
|
|
|
conf = scconf_new(path);
|
2003-12-03 14:09:15 +00:00
|
|
|
res = scconf_parse(conf);
|
2005-03-23 23:10:50 +00:00
|
|
|
|
2011-01-17 16:49:56 +00:00
|
|
|
sc_log(ctx, "profile %s loaded ok", path);
|
2005-03-23 23:10:50 +00:00
|
|
|
|
2015-04-29 21:22:29 +00:00
|
|
|
if (res < 0) {
|
|
|
|
scconf_free(conf);
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_FILE_NOT_FOUND);
|
2015-04-29 21:22:29 +00:00
|
|
|
}
|
2011-01-17 14:03:01 +00:00
|
|
|
|
2015-04-29 21:22:29 +00:00
|
|
|
if (res == 0) {
|
|
|
|
scconf_free(conf);
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_SYNTAX_ERROR);
|
2015-04-29 21:22:29 +00:00
|
|
|
}
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2003-11-19 20:37:02 +00:00
|
|
|
res = process_conf(profile, conf);
|
2002-04-03 11:52:30 +00:00
|
|
|
scconf_free(conf);
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, res);
|
2002-04-03 11:52:30 +00:00
|
|
|
}
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2011-01-17 14:51:10 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
int
|
2011-01-17 14:51:10 +00:00
|
|
|
sc_profile_finish(struct sc_profile *profile, const struct sc_app_info *app_info)
|
2002-04-03 11:52:30 +00:00
|
|
|
{
|
2011-01-17 14:03:01 +00:00
|
|
|
struct sc_context *ctx = profile->card->ctx;
|
2002-04-08 09:29:15 +00:00
|
|
|
struct file_info *fi;
|
|
|
|
struct pin_info *pi;
|
2003-10-13 16:13:12 +00:00
|
|
|
char reason[64];
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2011-01-17 14:51:10 +00:00
|
|
|
LOG_FUNC_CALLED(ctx);
|
2003-10-13 16:13:12 +00:00
|
|
|
profile->mf_info = sc_profile_find_file(profile, NULL, "MF");
|
2011-01-17 14:51:10 +00:00
|
|
|
if (!profile->mf_info)
|
|
|
|
LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "Profile doesn't define a MF");
|
|
|
|
|
|
|
|
if (app_info && app_info->aid.len) {
|
|
|
|
struct sc_path path;
|
|
|
|
|
2012-04-02 21:40:05 +00:00
|
|
|
sc_log(ctx, "finish profile with '%s' application profile", app_info->label);
|
2011-01-17 14:51:10 +00:00
|
|
|
memset(&path, 0, sizeof(struct sc_path));
|
|
|
|
path.type = SC_PATH_TYPE_DF_NAME;
|
|
|
|
path.aid = app_info->aid;
|
|
|
|
|
|
|
|
sc_log(ctx, "Look for file by path '%s'", sc_print_path(&path));
|
|
|
|
profile->df_info = sc_profile_find_file_by_path(profile, &path);
|
2012-04-02 21:40:05 +00:00
|
|
|
sc_log(ctx, "returned DF info %p", profile->df_info);
|
2011-03-20 12:18:55 +00:00
|
|
|
if (profile->df_info && profile->df_info->profile_extension) {
|
|
|
|
sc_log(ctx, "application profile extension '%s'", profile->df_info->profile_extension);
|
|
|
|
if (sc_profile_load(profile, profile->df_info->profile_extension))
|
|
|
|
LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "Cannot load application profile extension");
|
2011-01-17 14:51:10 +00:00
|
|
|
}
|
2003-10-13 16:13:12 +00:00
|
|
|
}
|
2012-04-02 21:40:05 +00:00
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
profile->df_info = sc_profile_find_file(profile, NULL, "PKCS15-AppDF");
|
2011-01-17 14:51:10 +00:00
|
|
|
if (!profile->df_info)
|
|
|
|
LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "Profile doesn't define a PKCS15-AppDF");
|
|
|
|
|
2003-10-21 11:05:35 +00:00
|
|
|
profile->p15_spec->file_app = profile->df_info->file;
|
2002-04-08 15:50:02 +00:00
|
|
|
profile->df_info->dont_free = 1;
|
2002-04-08 09:29:15 +00:00
|
|
|
|
|
|
|
for (pi = profile->pin_list; pi; pi = pi->next) {
|
2003-10-13 16:13:12 +00:00
|
|
|
const char *name;
|
|
|
|
|
2003-04-11 10:32:15 +00:00
|
|
|
set_pin_defaults(profile, pi);
|
2002-04-08 09:29:15 +00:00
|
|
|
if (!(name = pi->file_name))
|
|
|
|
continue;
|
2003-10-13 16:13:12 +00:00
|
|
|
if (!(fi = sc_profile_find_file(profile, NULL, name))) {
|
2011-01-17 14:03:01 +00:00
|
|
|
snprintf(reason, sizeof(reason), "unknown PIN file \"%s\"\n", name);
|
2003-10-13 16:13:12 +00:00
|
|
|
goto whine;
|
2002-04-08 09:29:15 +00:00
|
|
|
}
|
2011-01-17 14:51:10 +00:00
|
|
|
|
2002-04-08 09:29:15 +00:00
|
|
|
pi->file = fi;
|
|
|
|
}
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2012-04-02 21:40:05 +00:00
|
|
|
whine:
|
2011-01-17 16:49:56 +00:00
|
|
|
sc_log(ctx, "%s", reason);
|
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_INCONSISTENT_PROFILE);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
2002-04-05 10:05:50 +00:00
|
|
|
void
|
|
|
|
sc_profile_free(struct sc_profile *profile)
|
|
|
|
{
|
2002-04-05 14:55:37 +00:00
|
|
|
struct auth_info *ai;
|
|
|
|
struct pin_info *pi;
|
2004-10-24 17:17:48 +00:00
|
|
|
sc_macro_t *mi;
|
|
|
|
sc_template_t *ti;
|
|
|
|
|
|
|
|
if (profile->name)
|
|
|
|
free(profile->name);
|
2002-04-05 14:55:37 +00:00
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
free_file_list(&profile->ef_list);
|
2002-04-05 14:55:37 +00:00
|
|
|
|
|
|
|
while ((ai = profile->auth_list) != NULL) {
|
|
|
|
profile->auth_list = ai->next;
|
|
|
|
free(ai);
|
|
|
|
}
|
|
|
|
|
2004-10-24 17:17:48 +00:00
|
|
|
while ((ti = profile->template_list) != NULL) {
|
|
|
|
profile->template_list = ti->next;
|
|
|
|
if (ti->data)
|
2005-01-21 10:04:22 +00:00
|
|
|
sc_profile_free(ti->data);
|
2004-10-24 17:17:48 +00:00
|
|
|
if (ti->name)
|
|
|
|
free(ti->name);
|
|
|
|
free(ti);
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((mi = profile->macro_list) != NULL) {
|
|
|
|
profile->macro_list = mi->next;
|
|
|
|
if (mi->name)
|
|
|
|
free(mi->name);
|
|
|
|
free(mi);
|
|
|
|
}
|
|
|
|
|
2002-04-05 14:55:37 +00:00
|
|
|
while ((pi = profile->pin_list) != NULL) {
|
2004-10-24 17:17:48 +00:00
|
|
|
profile->pin_list = pi->next;
|
2002-04-08 15:50:02 +00:00
|
|
|
if (pi->file_name)
|
|
|
|
free(pi->file_name);
|
2002-04-05 14:55:37 +00:00
|
|
|
free(pi);
|
|
|
|
}
|
|
|
|
|
2003-10-21 11:05:35 +00:00
|
|
|
if (profile->p15_spec)
|
|
|
|
sc_pkcs15_card_free(profile->p15_spec);
|
2002-04-08 15:50:02 +00:00
|
|
|
memset(profile, 0, sizeof(*profile));
|
2002-04-05 10:05:50 +00:00
|
|
|
free(profile);
|
|
|
|
}
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
void
|
2003-10-13 16:13:12 +00:00
|
|
|
sc_profile_get_pin_info(struct sc_profile *profile,
|
2011-06-05 15:46:25 +00:00
|
|
|
int id, struct sc_pkcs15_auth_info *info)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
|
|
|
struct pin_info *pi;
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
pi = new_pin(profile, id);
|
2005-01-03 17:25:18 +00:00
|
|
|
if (pi == NULL)
|
|
|
|
return;
|
2013-07-08 17:08:56 +00:00
|
|
|
|
|
|
|
pi->pin.max_tries = pi->pin.tries_left;
|
2003-10-13 16:13:12 +00:00
|
|
|
*info = pi->pin;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
int
|
2010-03-05 10:37:11 +00:00
|
|
|
sc_profile_get_pin_retries(sc_profile_t *profile, int id)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
|
|
|
struct pin_info *pi;
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
pi = new_pin(profile, id);
|
2005-01-03 17:25:18 +00:00
|
|
|
if (pi == NULL)
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
2003-10-13 16:13:12 +00:00
|
|
|
return pi->pin.tries_left;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sc_profile_get_pin_id(struct sc_profile *profile,
|
2010-03-05 10:37:11 +00:00
|
|
|
unsigned int reference, int *id)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
|
|
|
struct pin_info *pi;
|
|
|
|
|
|
|
|
for (pi = profile->pin_list; pi; pi = pi->next) {
|
2011-06-05 15:46:25 +00:00
|
|
|
if (pi->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
|
|
|
|
continue;
|
|
|
|
if (pi->pin.attrs.pin.reference == (int)reference) {
|
2002-04-02 13:26:42 +00:00
|
|
|
*id = pi->id;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return SC_ERROR_OBJECT_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
int
|
|
|
|
sc_profile_get_file_in(sc_profile_t *profile,
|
|
|
|
const sc_path_t *path, const char *name, sc_file_t **ret)
|
|
|
|
{
|
|
|
|
struct file_info *fi;
|
|
|
|
|
|
|
|
if ((fi = sc_profile_find_file(profile, path, name)) == NULL)
|
|
|
|
return SC_ERROR_FILE_NOT_FOUND;
|
|
|
|
sc_file_dup(ret, fi->file);
|
2005-08-19 17:56:56 +00:00
|
|
|
if (*ret == NULL)
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
2003-10-13 16:13:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
int
|
|
|
|
sc_profile_get_file(struct sc_profile *profile,
|
2005-03-09 00:04:44 +00:00
|
|
|
const char *name, sc_file_t **ret)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
|
|
|
struct file_info *fi;
|
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
if ((fi = sc_profile_find_file(profile, NULL, name)) == NULL)
|
2002-04-02 13:26:42 +00:00
|
|
|
return SC_ERROR_FILE_NOT_FOUND;
|
|
|
|
sc_file_dup(ret, fi->file);
|
2005-08-19 17:56:56 +00:00
|
|
|
if (*ret == NULL)
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-21 09:41:40 +00:00
|
|
|
int
|
2012-04-02 21:40:05 +00:00
|
|
|
sc_profile_get_file_instance(struct sc_profile *profile, const char *name,
|
2010-01-21 09:41:40 +00:00
|
|
|
int index, sc_file_t **ret)
|
|
|
|
{
|
2011-01-02 14:27:42 +00:00
|
|
|
struct sc_context *ctx = profile->card->ctx;
|
2010-01-21 09:41:40 +00:00
|
|
|
struct file_info *fi;
|
|
|
|
struct sc_file *file;
|
|
|
|
int r;
|
|
|
|
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_CALLED(ctx);
|
|
|
|
sc_log(ctx, "try to get '%s' file instance", name);
|
2011-01-02 14:27:42 +00:00
|
|
|
|
2010-01-21 09:41:40 +00:00
|
|
|
if ((fi = sc_profile_find_file(profile, NULL, name)) == NULL)
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_FILE_NOT_FOUND);
|
2010-01-21 09:41:40 +00:00
|
|
|
sc_file_dup(&file, fi->file);
|
2011-01-17 16:49:56 +00:00
|
|
|
sc_log(ctx, "ident '%s'; parent '%s'", fi->ident, fi->parent->ident);
|
2010-01-21 09:41:40 +00:00
|
|
|
if (file == NULL)
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
|
|
|
|
sc_log(ctx, "file (type:%X, path:'%s')", file->type, sc_print_path(&file->path));
|
2010-01-21 09:41:40 +00:00
|
|
|
|
|
|
|
file->id += index;
|
2011-01-02 14:27:42 +00:00
|
|
|
if(file->type == SC_FILE_TYPE_BSO) {
|
|
|
|
r = sc_profile_add_file(profile, name, file);
|
2015-10-09 15:58:11 +00:00
|
|
|
if (r < 0)
|
|
|
|
sc_file_free(file);
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_TEST_RET(ctx, r, "Profile error: cannot add BSO file");
|
2011-01-02 14:27:42 +00:00
|
|
|
}
|
|
|
|
else if (file->path.len) {
|
|
|
|
file->path.value[file->path.len - 2] = (file->id >> 8) & 0xFF;
|
|
|
|
file->path.value[file->path.len - 1] = file->id & 0xFF;
|
2010-01-21 09:41:40 +00:00
|
|
|
|
2011-01-02 14:27:42 +00:00
|
|
|
r = sc_profile_add_file(profile, name, file);
|
2015-10-09 15:58:11 +00:00
|
|
|
if (r < 0)
|
|
|
|
sc_file_free(file);
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_TEST_RET(ctx, r, "Profile error: cannot add file");
|
2011-01-02 14:27:42 +00:00
|
|
|
}
|
2010-01-21 09:41:40 +00:00
|
|
|
|
|
|
|
if (ret)
|
|
|
|
*ret = file;
|
2016-02-17 23:16:10 +00:00
|
|
|
else
|
|
|
|
sc_file_free(file);
|
2010-01-21 09:41:40 +00:00
|
|
|
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
|
2010-01-21 09:41:40 +00:00
|
|
|
}
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
int
|
|
|
|
sc_profile_get_path(struct sc_profile *profile,
|
2005-03-09 00:04:44 +00:00
|
|
|
const char *name, sc_path_t *ret)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
|
|
|
struct file_info *fi;
|
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
if ((fi = sc_profile_find_file(profile, NULL, name)) == NULL)
|
2002-04-02 13:26:42 +00:00
|
|
|
return SC_ERROR_FILE_NOT_FOUND;
|
|
|
|
*ret = fi->file->path;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sc_profile_get_file_by_path(struct sc_profile *profile,
|
2005-03-09 00:04:44 +00:00
|
|
|
const sc_path_t *path, sc_file_t **ret)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2011-01-03 09:44:10 +00:00
|
|
|
struct sc_context *ctx = profile->card->ctx;
|
2002-04-02 13:26:42 +00:00
|
|
|
struct file_info *fi;
|
|
|
|
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_CALLED(ctx);
|
2002-04-02 13:26:42 +00:00
|
|
|
if ((fi = sc_profile_find_file_by_path(profile, path)) == NULL)
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_FILE_NOT_FOUND);
|
2002-04-02 13:26:42 +00:00
|
|
|
sc_file_dup(ret, fi->file);
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, *ret ? SC_SUCCESS : SC_ERROR_OUT_OF_MEMORY);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
int
|
|
|
|
sc_profile_add_file(sc_profile_t *profile, const char *name, sc_file_t *file)
|
|
|
|
{
|
2011-01-02 14:27:42 +00:00
|
|
|
struct sc_context *ctx = profile->card->ctx;
|
2003-10-13 16:13:12 +00:00
|
|
|
sc_path_t path = file->path;
|
2011-03-23 15:12:40 +00:00
|
|
|
struct file_info *parent;
|
2003-10-13 16:13:12 +00:00
|
|
|
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_CALLED(ctx);
|
2011-01-02 14:27:42 +00:00
|
|
|
if (!path.len) {
|
|
|
|
parent = profile->df_info;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
path.len -= 2;
|
|
|
|
parent = sc_profile_find_file_by_path(profile, &path);
|
2003-10-13 16:13:12 +00:00
|
|
|
}
|
2011-01-02 14:27:42 +00:00
|
|
|
if (!parent)
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_FILE_NOT_FOUND);
|
|
|
|
sc_log(ctx, "Parent path:%s", sc_print_path(&parent->file->path));
|
2011-01-02 14:27:42 +00:00
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
sc_file_dup(&file, file);
|
2005-08-19 17:56:56 +00:00
|
|
|
if (file == NULL)
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
|
2011-01-02 14:27:42 +00:00
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
add_file(profile, name, file, parent);
|
2011-01-17 16:49:56 +00:00
|
|
|
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
|
2003-10-13 16:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Instantiate template
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sc_profile_instantiate_template(sc_profile_t *profile,
|
|
|
|
const char *template_name, const sc_path_t *base_path,
|
|
|
|
const char *file_name, const sc_pkcs15_id_t *id,
|
|
|
|
sc_file_t **ret)
|
|
|
|
{
|
2011-01-17 14:03:01 +00:00
|
|
|
struct sc_context *ctx = profile->card->ctx;
|
|
|
|
struct sc_profile *tmpl;
|
|
|
|
struct sc_template *info;
|
2004-12-22 09:48:27 +00:00
|
|
|
unsigned int idx;
|
2003-10-13 16:13:12 +00:00
|
|
|
struct file_info *fi, *base_file, *match = NULL;
|
|
|
|
|
2010-01-16 20:55:45 +00:00
|
|
|
#ifdef DEBUG_PROFILE
|
2010-01-21 09:41:40 +00:00
|
|
|
printf("Instantiate %s in template %s\n", file_name, template_name);
|
2010-03-04 12:55:09 +00:00
|
|
|
sc_profile_find_file_by_path(profile, base_path);
|
2010-01-16 20:55:45 +00:00
|
|
|
#endif
|
2012-04-02 21:40:05 +00:00
|
|
|
for (info = profile->template_list; info; info = info->next)
|
2003-10-13 16:13:12 +00:00
|
|
|
if (!strcmp(info->name, template_name))
|
|
|
|
break;
|
2010-03-04 12:55:09 +00:00
|
|
|
if (info == NULL) {
|
2011-01-17 16:49:56 +00:00
|
|
|
sc_log(ctx, "Template %s not found", template_name);
|
2003-10-13 16:13:12 +00:00
|
|
|
return SC_ERROR_TEMPLATE_NOT_FOUND;
|
2010-03-04 12:55:09 +00:00
|
|
|
}
|
2003-10-13 16:13:12 +00:00
|
|
|
|
|
|
|
tmpl = info->data;
|
2004-12-22 09:48:27 +00:00
|
|
|
idx = id->value[id->len-1];
|
2003-10-13 16:13:12 +00:00
|
|
|
for (fi = profile->ef_list; fi; fi = fi->next) {
|
|
|
|
if (fi->base_template == tmpl
|
2004-12-22 09:48:27 +00:00
|
|
|
&& fi->inst_index == idx
|
2003-10-13 16:13:12 +00:00
|
|
|
&& sc_compare_path(&fi->inst_path, base_path)
|
|
|
|
&& !strcmp(fi->ident, file_name)) {
|
|
|
|
sc_file_dup(ret, fi->file);
|
2005-08-19 17:56:56 +00:00
|
|
|
if (*ret == NULL)
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
2003-10-13 16:13:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-17 16:49:56 +00:00
|
|
|
sc_log(ctx, "Instantiating template %s at %s", template_name, sc_print_path(base_path));
|
2003-10-13 16:13:12 +00:00
|
|
|
|
|
|
|
base_file = sc_profile_find_file_by_path(profile, base_path);
|
|
|
|
if (base_file == NULL) {
|
2011-01-17 16:49:56 +00:00
|
|
|
sc_log(ctx, "Directory %s not defined in profile", sc_print_path(base_path));
|
2003-10-13 16:13:12 +00:00
|
|
|
return SC_ERROR_OBJECT_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This loop relies on the fact that new files are always
|
|
|
|
* appended to the list, after the parent files they refer to
|
|
|
|
*/
|
|
|
|
assert(base_file->instance);
|
|
|
|
for (fi = tmpl->ef_list; fi; fi = fi->next) {
|
2011-03-23 15:12:40 +00:00
|
|
|
struct file_info *parent, *instance;
|
2003-10-13 16:13:12 +00:00
|
|
|
unsigned int skew = 0;
|
|
|
|
|
|
|
|
fi->instance = NULL;
|
|
|
|
if ((parent = fi->parent) == NULL) {
|
|
|
|
parent = base_file;
|
2004-12-22 09:48:27 +00:00
|
|
|
skew = idx;
|
2003-10-13 16:13:12 +00:00
|
|
|
}
|
|
|
|
parent = parent->instance;
|
|
|
|
|
|
|
|
instance = sc_profile_instantiate_file(profile, fi, parent, skew);
|
2005-01-03 17:25:18 +00:00
|
|
|
if (instance == NULL)
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
2003-10-13 16:13:12 +00:00
|
|
|
instance->base_template = tmpl;
|
2004-12-22 09:48:27 +00:00
|
|
|
instance->inst_index = idx;
|
2003-10-13 16:13:12 +00:00
|
|
|
instance->inst_path = *base_path;
|
|
|
|
|
|
|
|
if (!strcmp(instance->ident, file_name))
|
|
|
|
match = instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match == NULL) {
|
2011-01-17 16:49:56 +00:00
|
|
|
sc_log(ctx, "No file named \"%s\" in template \"%s\"",
|
2003-10-13 16:13:12 +00:00
|
|
|
file_name, template_name);
|
|
|
|
return SC_ERROR_OBJECT_NOT_FOUND;
|
|
|
|
}
|
|
|
|
sc_file_dup(ret, match->file);
|
2005-08-19 17:56:56 +00:00
|
|
|
if (*ret == NULL)
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
2010-01-16 20:55:45 +00:00
|
|
|
#ifdef DEBUG_PROFILE
|
|
|
|
printf("Template instantiated\n");
|
|
|
|
#endif
|
2003-10-13 16:13:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-03-23 15:12:40 +00:00
|
|
|
static struct file_info *
|
|
|
|
sc_profile_instantiate_file(sc_profile_t *profile, struct file_info *ft,
|
|
|
|
struct file_info *parent, unsigned int skew)
|
2003-10-13 16:13:12 +00:00
|
|
|
{
|
2011-01-17 14:03:01 +00:00
|
|
|
struct sc_context *ctx = profile->card->ctx;
|
2003-10-13 16:13:12 +00:00
|
|
|
struct file_info *fi;
|
|
|
|
|
Do not cast the return value of malloc(3) and calloc(3)
From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety
" Casting and type safety
malloc returns a void pointer (void *), which indicates that it is a
pointer to a region of unknown data type. One may "cast" (see type
conversion) this pointer to a specific type, as in
int *ptr = (int*)malloc(10 * sizeof (int));
When using C, this is considered bad practice; it is redundant under the
C standard. Moreover, putting in a cast may mask failure to include the
header stdlib.h, in which the prototype for malloc is found. In the
absence of a prototype for malloc, the C compiler will assume that
malloc returns an int, and will issue a warning in a context such as the
above, provided the error is not masked by a cast. On certain
architectures and data models (such as LP64 on 64 bit systems, where
long and pointers are 64 bit and int is 32 bit), this error can actually
result in undefined behavior, as the implicitly declared malloc returns
a 32 bit value whereas the actually defined function returns a 64 bit
value. Depending on calling conventions and memory layout, this may
result in stack smashing.
The returned pointer need not be explicitly cast to a more specific
pointer type, since ANSI C defines an implicit conversion between the
void pointer type and other pointers to objects. An explicit cast of
malloc's return value is sometimes performed because malloc originally
returned a char *, but this cast is unnecessary in standard C
code.[4][5] Omitting the cast, however, creates an incompatibility with
C++, which does require it.
The lack of a specific pointer type returned from malloc is type-unsafe
behaviour: malloc allocates based on byte count but not on type. This
distinguishes it from the C++ new operator that returns a pointer whose
type relies on the operand. (see C Type Safety). "
See also
http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
|
|
|
fi = calloc(1, sizeof(*fi));
|
2005-01-03 17:25:18 +00:00
|
|
|
if (fi == NULL)
|
|
|
|
return NULL;
|
2003-10-13 16:13:12 +00:00
|
|
|
fi->instance = fi;
|
|
|
|
fi->parent = parent;
|
|
|
|
fi->ident = strdup(ft->ident);
|
2005-08-19 17:56:56 +00:00
|
|
|
if (fi->ident == NULL) {
|
|
|
|
free(fi);
|
|
|
|
return NULL;
|
|
|
|
}
|
2003-10-13 16:13:12 +00:00
|
|
|
sc_file_dup(&fi->file, ft->file);
|
2005-08-19 17:56:56 +00:00
|
|
|
if (fi->file == NULL) {
|
|
|
|
free(fi->ident);
|
|
|
|
free(fi);
|
|
|
|
return NULL;
|
|
|
|
}
|
2003-10-13 16:13:12 +00:00
|
|
|
fi->file->path = parent->file->path;
|
|
|
|
fi->file->id += skew;
|
2010-03-04 12:55:09 +00:00
|
|
|
|
2012-04-02 21:40:05 +00:00
|
|
|
if (fi->file->type == SC_FILE_TYPE_INTERNAL_EF
|
2010-03-04 12:55:09 +00:00
|
|
|
|| fi->file->type == SC_FILE_TYPE_WORKING_EF
|
|
|
|
|| (fi->file->type == SC_FILE_TYPE_DF && fi->file->id))
|
2010-01-21 09:41:40 +00:00
|
|
|
sc_append_file_id(&fi->file->path, fi->file->id);
|
2003-10-13 16:13:12 +00:00
|
|
|
|
|
|
|
append_file(profile, fi);
|
|
|
|
|
|
|
|
ft->instance = fi;
|
|
|
|
|
2011-01-17 16:49:56 +00:00
|
|
|
sc_log(ctx, "Instantiated %s at %s", ft->ident, sc_print_path(&fi->file->path));
|
|
|
|
sc_log(ctx, " parent=%s@%s", parent->ident, sc_print_path(&parent->file->path));
|
2003-10-13 16:13:12 +00:00
|
|
|
|
|
|
|
return fi;
|
|
|
|
}
|
|
|
|
|
2010-02-20 22:04:07 +00:00
|
|
|
int
|
2012-04-02 21:40:05 +00:00
|
|
|
sc_profile_get_pin_id_by_reference(struct sc_profile *profile,
|
|
|
|
unsigned auth_method, int reference,
|
2011-06-05 15:46:25 +00:00
|
|
|
struct sc_pkcs15_auth_info *auth_info)
|
2010-02-20 22:04:07 +00:00
|
|
|
{
|
|
|
|
struct pin_info *pinfo;
|
|
|
|
|
|
|
|
for (pinfo = profile->pin_list; pinfo; pinfo = pinfo->next) {
|
|
|
|
if (auth_method == SC_AC_SYMBOLIC) {
|
|
|
|
if (pinfo->id != reference)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
2011-06-05 15:46:25 +00:00
|
|
|
if (pinfo->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
|
|
|
|
continue;
|
2010-02-20 22:04:07 +00:00
|
|
|
if (pinfo->pin.auth_method != auth_method)
|
|
|
|
continue;
|
2011-06-05 15:46:25 +00:00
|
|
|
if (pinfo->pin.attrs.pin.reference != reference)
|
2010-02-20 22:04:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-06-05 15:46:25 +00:00
|
|
|
if (auth_info)
|
|
|
|
*auth_info = pinfo->pin;
|
2010-02-20 22:04:07 +00:00
|
|
|
return pinfo->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
/*
|
|
|
|
* Configuration file parser
|
|
|
|
*/
|
|
|
|
static void
|
2010-03-04 12:55:09 +00:00
|
|
|
init_state(struct state *cur_state, struct state *new_state)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
memset(new_state, 0, sizeof(*new_state));
|
2010-03-04 12:55:09 +00:00
|
|
|
new_state->filename = cur_state->filename;
|
|
|
|
new_state->profile = cur_state->profile;
|
|
|
|
new_state->frame = cur_state;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_card_driver(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
cur->profile->driver = strdup(argv[0]);
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_maxpinlength(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
return get_uint(cur, argv[0], &cur->profile->pin_maxlen);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_minpinlength(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
return get_uint(cur, argv[0], &cur->profile->pin_minlen);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_default_pin_type(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
return map_str2int(cur, argv[0],
|
|
|
|
&cur->profile->pin_encoding, pinTypeNames);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_pin_pad_char(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
return get_uint(cur, argv[0], &cur->profile->pin_pad_char);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
static int
|
|
|
|
do_pin_domains(struct state *cur, int argc, char **argv)
|
|
|
|
{
|
|
|
|
return get_bool(cur, argv[0], &cur->profile->pin_domains);
|
|
|
|
}
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_card_label(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2003-10-21 11:05:35 +00:00
|
|
|
struct sc_pkcs15_card *p15card = cur->profile->p15_spec;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2010-10-05 15:44:58 +00:00
|
|
|
return setstr(&p15card->tokeninfo->label, argv[0]);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_card_manufacturer(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2003-10-21 11:05:35 +00:00
|
|
|
struct sc_pkcs15_card *p15card = cur->profile->p15_spec;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2010-10-05 15:44:58 +00:00
|
|
|
return setstr(&p15card->tokeninfo->manufacturer_id, argv[0]);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
2003-11-19 20:37:02 +00:00
|
|
|
/*
|
|
|
|
* Command related to the pkcs15 we generate
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
do_direct_certificates(struct state *cur, int argc, char **argv)
|
|
|
|
{
|
|
|
|
return get_bool(cur, argv[0], &cur->profile->pkcs15.direct_certificates);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
do_encode_df_length(struct state *cur, int argc, char **argv)
|
|
|
|
{
|
|
|
|
return get_bool(cur, argv[0], &cur->profile->pkcs15.encode_df_length);
|
|
|
|
}
|
|
|
|
|
2005-08-24 09:50:48 +00:00
|
|
|
static int
|
2005-08-29 20:48:00 +00:00
|
|
|
do_encode_update_field(struct state *cur, int argc, char **argv)
|
2005-08-24 09:50:48 +00:00
|
|
|
{
|
2005-08-29 20:48:00 +00:00
|
|
|
return get_bool(cur, argv[0], &cur->profile->pkcs15.do_last_update);
|
2005-08-24 09:50:48 +00:00
|
|
|
}
|
|
|
|
|
2009-11-13 09:45:21 +00:00
|
|
|
static int
|
|
|
|
do_pkcs15_id_style(struct state *cur, int argc, char **argv)
|
|
|
|
{
|
|
|
|
return map_str2int(cur, argv[0], &cur->profile->id_style, idStyleNames);
|
|
|
|
}
|
|
|
|
|
2012-05-26 07:17:21 +00:00
|
|
|
static int
|
|
|
|
do_minidriver_support_style(struct state *cur, int argc, char **argv)
|
|
|
|
{
|
|
|
|
return map_str2int(cur, argv[0], &cur->profile->md_style, mdStyleNames);
|
|
|
|
}
|
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
/*
|
|
|
|
* Process an option block
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
process_option(struct state *cur, struct block *info,
|
|
|
|
const char *name, scconf_block *blk)
|
|
|
|
{
|
2003-11-19 20:37:02 +00:00
|
|
|
sc_profile_t *profile = cur->profile;
|
|
|
|
int match = 0, i;
|
|
|
|
|
|
|
|
for (i = 0; profile->options[i]; i++)
|
|
|
|
match |= !strcmp(profile->options[i], name);
|
|
|
|
if (!match && strcmp("default", name))
|
2003-10-13 16:13:12 +00:00
|
|
|
return 0;
|
|
|
|
return process_block(cur, info, name, blk);
|
|
|
|
}
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
/*
|
|
|
|
* Process a key block
|
|
|
|
*/
|
2002-04-02 13:26:42 +00:00
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
process_key(struct state *cur, struct block *info,
|
|
|
|
const char *name, scconf_block *blk)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
unsigned int type, id;
|
|
|
|
struct state state;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
if (get_authid(cur, name, &type, &id))
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
2002-04-03 11:52:30 +00:00
|
|
|
|
|
|
|
init_state(cur, &state);
|
|
|
|
state.key = new_key(cur->profile, type, id);
|
|
|
|
return process_block(&state, info, name, blk);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct auth_info *
|
|
|
|
new_key(struct sc_profile *profile, unsigned int type, unsigned int ref)
|
|
|
|
{
|
|
|
|
struct auth_info *ai, **aip;
|
|
|
|
|
|
|
|
for (aip = &profile->auth_list; (ai = *aip); aip = &ai->next) {
|
|
|
|
if (ai->type == type && ai->ref == ref)
|
|
|
|
return ai;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
2002-04-03 11:52:30 +00:00
|
|
|
|
Do not cast the return value of malloc(3) and calloc(3)
From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety
" Casting and type safety
malloc returns a void pointer (void *), which indicates that it is a
pointer to a region of unknown data type. One may "cast" (see type
conversion) this pointer to a specific type, as in
int *ptr = (int*)malloc(10 * sizeof (int));
When using C, this is considered bad practice; it is redundant under the
C standard. Moreover, putting in a cast may mask failure to include the
header stdlib.h, in which the prototype for malloc is found. In the
absence of a prototype for malloc, the C compiler will assume that
malloc returns an int, and will issue a warning in a context such as the
above, provided the error is not masked by a cast. On certain
architectures and data models (such as LP64 on 64 bit systems, where
long and pointers are 64 bit and int is 32 bit), this error can actually
result in undefined behavior, as the implicitly declared malloc returns
a 32 bit value whereas the actually defined function returns a 64 bit
value. Depending on calling conventions and memory layout, this may
result in stack smashing.
The returned pointer need not be explicitly cast to a more specific
pointer type, since ANSI C defines an implicit conversion between the
void pointer type and other pointers to objects. An explicit cast of
malloc's return value is sometimes performed because malloc originally
returned a char *, but this cast is unnecessary in standard C
code.[4][5] Omitting the cast, however, creates an incompatibility with
C++, which does require it.
The lack of a specific pointer type returned from malloc is type-unsafe
behaviour: malloc allocates based on byte count but not on type. This
distinguishes it from the C++ new operator that returns a pointer whose
type relies on the operand. (see C Type Safety). "
See also
http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
|
|
|
ai = calloc(1, sizeof(*ai));
|
2005-01-03 17:25:18 +00:00
|
|
|
if (ai == NULL)
|
|
|
|
return NULL;
|
2002-04-03 11:52:30 +00:00
|
|
|
ai->type = type;
|
|
|
|
ai->ref = ref;
|
|
|
|
*aip = ai;
|
|
|
|
return ai;
|
|
|
|
}
|
|
|
|
|
2004-12-22 09:48:27 +00:00
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_key_value(struct state *cur, int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct auth_info *ai = cur->key;
|
|
|
|
const char *key = argv[0];
|
|
|
|
size_t key_len;
|
|
|
|
unsigned char keybuf[32];
|
|
|
|
|
|
|
|
if (key[0] == '=') {
|
|
|
|
++key;
|
|
|
|
key_len = strlen(key);
|
|
|
|
memcpy(keybuf, key, key_len);
|
|
|
|
} else {
|
|
|
|
key_len = sizeof(keybuf);
|
|
|
|
if (sc_hex_to_bin(key, keybuf, &key_len)) {
|
|
|
|
parse_error(cur, "Error parsing PIN/key \"%s\"\n", key);
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
2002-04-03 11:52:30 +00:00
|
|
|
}
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
2002-04-03 11:52:30 +00:00
|
|
|
|
2002-05-21 19:41:09 +00:00
|
|
|
memcpy(ai->key, keybuf, key_len);
|
2002-04-03 11:52:30 +00:00
|
|
|
ai->key_len = key_len;
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
/*
|
|
|
|
* This function is called when the parser finds a block with an unknown
|
|
|
|
* name in the filesystem block. This will create a new filesystem
|
|
|
|
* object as the child of the current object.
|
|
|
|
*/
|
2002-04-02 13:26:42 +00:00
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
process_df(struct state *cur, struct block *info,
|
|
|
|
const char *name, scconf_block *blk)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
struct state state;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
init_state(cur, &state);
|
|
|
|
if (name == NULL) {
|
|
|
|
parse_error(cur, "No name given for DF object.");
|
|
|
|
return 1;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
2002-04-03 11:52:30 +00:00
|
|
|
if (!(state.file = new_file(cur, name, SC_FILE_TYPE_DF)))
|
|
|
|
return 1;
|
|
|
|
return process_block(&state, info, name, blk);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
process_ef(struct state *cur, struct block *info,
|
|
|
|
const char *name, scconf_block *blk)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
struct state state;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
init_state(cur, &state);
|
|
|
|
if (name == NULL) {
|
|
|
|
parse_error(cur, "No name given for EF object.");
|
|
|
|
return 1;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
2002-04-03 11:52:30 +00:00
|
|
|
if (!(state.file = new_file(cur, name, SC_FILE_TYPE_WORKING_EF)))
|
|
|
|
return 1;
|
|
|
|
return process_block(&state, info, name, blk);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
2010-01-16 20:55:45 +00:00
|
|
|
|
2010-01-21 09:41:40 +00:00
|
|
|
static int
|
|
|
|
process_bso(struct state *cur, struct block *info,
|
|
|
|
const char *name, scconf_block *blk)
|
|
|
|
{
|
|
|
|
struct state state;
|
|
|
|
|
|
|
|
init_state(cur, &state);
|
|
|
|
if (name == NULL) {
|
|
|
|
parse_error(cur, "No name given for BSO object.");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (!(state.file = new_file(cur, name, SC_FILE_TYPE_BSO)))
|
|
|
|
return 1;
|
|
|
|
return process_block(&state, info, name, blk);
|
|
|
|
}
|
|
|
|
|
2012-04-02 21:40:05 +00:00
|
|
|
/*
|
|
|
|
* In the template the difference between any two file-ids
|
2010-01-16 20:55:45 +00:00
|
|
|
* should be greater then TEMPLATE_FILEID_MIN_DIFF.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
template_sanity_check(struct state *cur, struct sc_profile *templ)
|
|
|
|
{
|
|
|
|
struct file_info *fi, *ffi;
|
|
|
|
|
|
|
|
for (fi = templ->ef_list; fi; fi = fi->next) {
|
|
|
|
struct sc_path fi_path = fi->file->path;
|
2012-04-02 21:40:05 +00:00
|
|
|
int fi_id = fi_path.value[fi_path.len - 2] * 0x100
|
|
|
|
+ fi_path.value[fi_path.len - 1];
|
2010-01-16 20:55:45 +00:00
|
|
|
|
2010-01-21 09:41:40 +00:00
|
|
|
if (fi->file->type == SC_FILE_TYPE_BSO)
|
2010-01-16 20:55:45 +00:00
|
|
|
continue;
|
|
|
|
for (ffi = templ->ef_list; ffi; ffi = ffi->next) {
|
|
|
|
struct sc_path ffi_path = ffi->file->path;
|
2012-04-02 21:40:05 +00:00
|
|
|
int dlt, ffi_id = ffi_path.value[ffi_path.len - 2] * 0x100
|
|
|
|
+ ffi_path.value[ffi_path.len - 1];
|
2010-01-16 20:55:45 +00:00
|
|
|
|
2010-01-21 09:41:40 +00:00
|
|
|
if (ffi->file->type == SC_FILE_TYPE_BSO)
|
2010-01-16 20:55:45 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
dlt = fi_id > ffi_id ? fi_id - ffi_id : ffi_id - fi_id;
|
|
|
|
if (strcmp(ffi->ident, fi->ident)) {
|
2010-02-15 17:47:20 +00:00
|
|
|
if (dlt >= TEMPLATE_FILEID_MIN_DIFF)
|
2010-01-16 20:55:45 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
parse_error(cur, "Template insane: file-ids should be substantially different");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SC_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
static int
|
|
|
|
process_tmpl(struct state *cur, struct block *info,
|
|
|
|
const char *name, scconf_block *blk)
|
|
|
|
{
|
|
|
|
struct state state;
|
|
|
|
sc_template_t *tinfo;
|
|
|
|
sc_profile_t *templ;
|
2010-01-16 20:55:45 +00:00
|
|
|
int r;
|
2003-10-13 16:13:12 +00:00
|
|
|
|
2010-01-16 20:55:45 +00:00
|
|
|
#ifdef DEBUG_PROFILE
|
2010-03-04 12:55:09 +00:00
|
|
|
printf("Process template:%s; block:%s\n", name, info->name);
|
2010-01-16 20:55:45 +00:00
|
|
|
#endif
|
2003-10-13 16:13:12 +00:00
|
|
|
if (name == NULL) {
|
|
|
|
parse_error(cur, "No name given for template.");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
Do not cast the return value of malloc(3) and calloc(3)
From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety
" Casting and type safety
malloc returns a void pointer (void *), which indicates that it is a
pointer to a region of unknown data type. One may "cast" (see type
conversion) this pointer to a specific type, as in
int *ptr = (int*)malloc(10 * sizeof (int));
When using C, this is considered bad practice; it is redundant under the
C standard. Moreover, putting in a cast may mask failure to include the
header stdlib.h, in which the prototype for malloc is found. In the
absence of a prototype for malloc, the C compiler will assume that
malloc returns an int, and will issue a warning in a context such as the
above, provided the error is not masked by a cast. On certain
architectures and data models (such as LP64 on 64 bit systems, where
long and pointers are 64 bit and int is 32 bit), this error can actually
result in undefined behavior, as the implicitly declared malloc returns
a 32 bit value whereas the actually defined function returns a 64 bit
value. Depending on calling conventions and memory layout, this may
result in stack smashing.
The returned pointer need not be explicitly cast to a more specific
pointer type, since ANSI C defines an implicit conversion between the
void pointer type and other pointers to objects. An explicit cast of
malloc's return value is sometimes performed because malloc originally
returned a char *, but this cast is unnecessary in standard C
code.[4][5] Omitting the cast, however, creates an incompatibility with
C++, which does require it.
The lack of a specific pointer type returned from malloc is type-unsafe
behaviour: malloc allocates based on byte count but not on type. This
distinguishes it from the C++ new operator that returns a pointer whose
type relies on the operand. (see C Type Safety). "
See also
http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
|
|
|
templ = calloc(1, sizeof(*templ));
|
2005-01-03 17:25:18 +00:00
|
|
|
if (templ == NULL) {
|
|
|
|
parse_error(cur, "memory allocation failed");
|
|
|
|
return 1;
|
|
|
|
}
|
2012-04-02 21:40:05 +00:00
|
|
|
|
Do not cast the return value of malloc(3) and calloc(3)
From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety
" Casting and type safety
malloc returns a void pointer (void *), which indicates that it is a
pointer to a region of unknown data type. One may "cast" (see type
conversion) this pointer to a specific type, as in
int *ptr = (int*)malloc(10 * sizeof (int));
When using C, this is considered bad practice; it is redundant under the
C standard. Moreover, putting in a cast may mask failure to include the
header stdlib.h, in which the prototype for malloc is found. In the
absence of a prototype for malloc, the C compiler will assume that
malloc returns an int, and will issue a warning in a context such as the
above, provided the error is not masked by a cast. On certain
architectures and data models (such as LP64 on 64 bit systems, where
long and pointers are 64 bit and int is 32 bit), this error can actually
result in undefined behavior, as the implicitly declared malloc returns
a 32 bit value whereas the actually defined function returns a 64 bit
value. Depending on calling conventions and memory layout, this may
result in stack smashing.
The returned pointer need not be explicitly cast to a more specific
pointer type, since ANSI C defines an implicit conversion between the
void pointer type and other pointers to objects. An explicit cast of
malloc's return value is sometimes performed because malloc originally
returned a char *, but this cast is unnecessary in standard C
code.[4][5] Omitting the cast, however, creates an incompatibility with
C++, which does require it.
The lack of a specific pointer type returned from malloc is type-unsafe
behaviour: malloc allocates based on byte count but not on type. This
distinguishes it from the C++ new operator that returns a pointer whose
type relies on the operand. (see C Type Safety). "
See also
http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
|
|
|
tinfo = calloc(1, sizeof(*tinfo));
|
2005-01-03 17:25:18 +00:00
|
|
|
if (tinfo == NULL) {
|
|
|
|
parse_error(cur, "memory allocation failed");
|
|
|
|
free(templ);
|
|
|
|
return 1;
|
|
|
|
}
|
2003-10-13 16:13:12 +00:00
|
|
|
tinfo->name = strdup(name);
|
|
|
|
tinfo->data = templ;
|
|
|
|
|
|
|
|
tinfo->next = cur->profile->template_list;
|
|
|
|
cur->profile->template_list = tinfo;
|
|
|
|
|
|
|
|
init_state(cur, &state);
|
|
|
|
state.profile = tinfo->data;
|
|
|
|
state.file = NULL;
|
|
|
|
|
2010-01-16 20:55:45 +00:00
|
|
|
r = process_block(&state, info, name, blk);
|
|
|
|
if (!r)
|
|
|
|
r = template_sanity_check(cur, templ);
|
|
|
|
|
|
|
|
#ifdef DEBUG_PROFILE
|
2010-03-04 12:55:09 +00:00
|
|
|
printf("Template %s processed; returns %i\n", name, r);
|
2010-01-16 20:55:45 +00:00
|
|
|
#endif
|
|
|
|
return r;
|
2003-10-13 16:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Append new file at the end of the ef_list.
|
|
|
|
* This is crucial; the profile instantiation code relies on it
|
|
|
|
*/
|
2007-06-21 11:07:00 +00:00
|
|
|
static void append_file(sc_profile_t *profile, struct file_info *nfile)
|
2003-10-13 16:13:12 +00:00
|
|
|
{
|
|
|
|
struct file_info **list, *fi;
|
|
|
|
|
|
|
|
list = &profile->ef_list;
|
|
|
|
while ((fi = *list) != NULL)
|
|
|
|
list = &fi->next;
|
2004-12-22 09:48:27 +00:00
|
|
|
*list = nfile;
|
2003-10-13 16:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a new file to the profile.
|
|
|
|
* This function is called by sc_profile_add_file.
|
|
|
|
*/
|
2011-03-23 15:12:40 +00:00
|
|
|
static struct file_info *
|
2003-10-13 16:13:12 +00:00
|
|
|
add_file(sc_profile_t *profile, const char *name,
|
2011-03-23 15:12:40 +00:00
|
|
|
sc_file_t *file, struct file_info *parent)
|
2003-10-13 16:13:12 +00:00
|
|
|
{
|
2011-03-23 15:12:40 +00:00
|
|
|
struct file_info *info;
|
2003-10-13 16:13:12 +00:00
|
|
|
|
Do not cast the return value of malloc(3) and calloc(3)
From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety
" Casting and type safety
malloc returns a void pointer (void *), which indicates that it is a
pointer to a region of unknown data type. One may "cast" (see type
conversion) this pointer to a specific type, as in
int *ptr = (int*)malloc(10 * sizeof (int));
When using C, this is considered bad practice; it is redundant under the
C standard. Moreover, putting in a cast may mask failure to include the
header stdlib.h, in which the prototype for malloc is found. In the
absence of a prototype for malloc, the C compiler will assume that
malloc returns an int, and will issue a warning in a context such as the
above, provided the error is not masked by a cast. On certain
architectures and data models (such as LP64 on 64 bit systems, where
long and pointers are 64 bit and int is 32 bit), this error can actually
result in undefined behavior, as the implicitly declared malloc returns
a 32 bit value whereas the actually defined function returns a 64 bit
value. Depending on calling conventions and memory layout, this may
result in stack smashing.
The returned pointer need not be explicitly cast to a more specific
pointer type, since ANSI C defines an implicit conversion between the
void pointer type and other pointers to objects. An explicit cast of
malloc's return value is sometimes performed because malloc originally
returned a char *, but this cast is unnecessary in standard C
code.[4][5] Omitting the cast, however, creates an incompatibility with
C++, which does require it.
The lack of a specific pointer type returned from malloc is type-unsafe
behaviour: malloc allocates based on byte count but not on type. This
distinguishes it from the C++ new operator that returns a pointer whose
type relies on the operand. (see C Type Safety). "
See also
http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
|
|
|
info = calloc(1, sizeof(*info));
|
2005-01-03 17:25:18 +00:00
|
|
|
if (info == NULL)
|
|
|
|
return NULL;
|
2003-10-13 16:13:12 +00:00
|
|
|
info->instance = info;
|
|
|
|
info->ident = strdup(name);
|
|
|
|
|
|
|
|
info->parent = parent;
|
|
|
|
info->file = file;
|
|
|
|
|
|
|
|
append_file(profile, info);
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free file_info list
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
free_file_list(struct file_info **list)
|
|
|
|
{
|
|
|
|
struct file_info *fi;
|
|
|
|
|
|
|
|
while ((fi = *list) != NULL) {
|
|
|
|
*list = fi->next;
|
|
|
|
|
|
|
|
if (fi->dont_free == 0)
|
|
|
|
sc_file_free(fi->file);
|
|
|
|
free(fi->ident);
|
|
|
|
free(fi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a new file info object.
|
|
|
|
* This function is called by the profile parser.
|
|
|
|
*/
|
2002-04-03 11:52:30 +00:00
|
|
|
static struct file_info *
|
|
|
|
new_file(struct state *cur, const char *name, unsigned int type)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2003-10-13 16:13:12 +00:00
|
|
|
sc_profile_t *profile = cur->profile;
|
2011-03-23 15:12:40 +00:00
|
|
|
struct file_info *info;
|
2003-10-13 16:13:12 +00:00
|
|
|
sc_file_t *file;
|
2002-04-08 15:50:02 +00:00
|
|
|
unsigned int df_type = 0, dont_free = 0;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
if ((info = sc_profile_find_file(profile, NULL, name)) != NULL)
|
2002-04-03 11:52:30 +00:00
|
|
|
return info;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
|
|
|
/* Special cases for those EFs handled separately
|
|
|
|
* by the PKCS15 logic */
|
|
|
|
if (strncasecmp(name, "PKCS15-", 7)) {
|
2002-04-03 11:52:30 +00:00
|
|
|
file = init_file(type);
|
2002-04-02 13:26:42 +00:00
|
|
|
} else if (!strcasecmp(name+7, "TokenInfo")) {
|
2003-10-21 11:05:35 +00:00
|
|
|
file = profile->p15_spec->file_tokeninfo;
|
2002-04-08 15:50:02 +00:00
|
|
|
dont_free = 1;
|
2002-04-02 13:26:42 +00:00
|
|
|
} else if (!strcasecmp(name+7, "ODF")) {
|
2003-10-21 11:05:35 +00:00
|
|
|
file = profile->p15_spec->file_odf;
|
2002-04-08 15:50:02 +00:00
|
|
|
dont_free = 1;
|
2005-12-17 19:53:12 +00:00
|
|
|
} else if (!strcasecmp(name+7, "UnusedSpace")) {
|
|
|
|
file = profile->p15_spec->file_unusedspace;
|
|
|
|
dont_free = 1;
|
2002-04-03 11:52:30 +00:00
|
|
|
} else if (!strcasecmp(name+7, "AppDF")) {
|
|
|
|
file = init_file(SC_FILE_TYPE_DF);
|
2002-04-02 13:26:42 +00:00
|
|
|
} else {
|
2002-04-03 11:52:30 +00:00
|
|
|
if (map_str2int(cur, name+7, &df_type, pkcs15DfNames))
|
|
|
|
return NULL;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
|
|
|
file = init_file(SC_FILE_TYPE_WORKING_EF);
|
2002-04-03 11:52:30 +00:00
|
|
|
profile->df[df_type] = file;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
assert(file);
|
2011-04-19 12:59:23 +00:00
|
|
|
if (file->type != type) {
|
2002-04-03 11:52:30 +00:00
|
|
|
parse_error(cur, "inconsistent file type (should be %s)",
|
2012-04-02 21:40:05 +00:00
|
|
|
file->type == SC_FILE_TYPE_DF
|
|
|
|
? "DF" : file->type == SC_FILE_TYPE_BSO
|
2010-01-21 09:41:40 +00:00
|
|
|
? "BS0" : "EF");
|
2005-12-05 21:55:03 +00:00
|
|
|
if (strncasecmp(name, "PKCS15-", 7) ||
|
2012-04-02 21:40:05 +00:00
|
|
|
!strcasecmp(name+7, "AppDF"))
|
2005-12-05 21:55:03 +00:00
|
|
|
sc_file_free(file);
|
2002-04-03 11:52:30 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
info = add_file(profile, name, file, cur->file);
|
2005-01-03 17:25:18 +00:00
|
|
|
if (info == NULL) {
|
|
|
|
parse_error(cur, "memory allocation failed");
|
|
|
|
return NULL;
|
|
|
|
}
|
2002-04-08 15:50:02 +00:00
|
|
|
info->dont_free = dont_free;
|
2002-04-03 11:52:30 +00:00
|
|
|
return info;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_file_type(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-05 08:45:14 +00:00
|
|
|
unsigned int type;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-05 08:45:14 +00:00
|
|
|
if (map_str2int(cur, argv[0], &type, fileTypeNames))
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
2002-04-05 08:45:14 +00:00
|
|
|
cur->file->file->type = type;
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_file_path(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
struct sc_file *file = cur->file->file;
|
|
|
|
struct sc_path *path = &file->path;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
/* sc_format_path doesn't return an error indication
|
|
|
|
* when it's unable to parse the path */
|
|
|
|
sc_format_path(argv[0], path);
|
|
|
|
if (!path->len || (path->len & 1)) {
|
|
|
|
parse_error(cur, "Invalid path length\n");
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2011-01-17 14:03:01 +00:00
|
|
|
file->id = (path->value[path->len-2] << 8) | path->value[path->len-1];
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_fileid(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
struct file_info *fi;
|
|
|
|
struct sc_file *df, *file = cur->file->file;
|
2002-04-02 13:26:42 +00:00
|
|
|
struct sc_path temp, *path = &file->path;
|
|
|
|
|
|
|
|
/* sc_format_path doesn't return an error indication
|
|
|
|
* when it's unable to parse the path */
|
|
|
|
sc_format_path(argv[0], &temp);
|
|
|
|
if (temp.len != 2) {
|
2002-04-03 11:52:30 +00:00
|
|
|
parse_error(cur, "Invalid file ID length\n");
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
/* Get the DF, if any */
|
|
|
|
if ((fi = cur->file->parent) && (df = fi->file)) {
|
2011-01-17 16:05:43 +00:00
|
|
|
if (!df->path.len && !df->path.aid.len) {
|
2002-04-03 11:52:30 +00:00
|
|
|
parse_error(cur, "No path/fileid set for parent DF\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2015-02-04 08:24:50 +00:00
|
|
|
if (df->path.len + 2 > sizeof(df->path.value)) {
|
2002-04-03 11:52:30 +00:00
|
|
|
parse_error(cur, "File path too long\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
*path = df->path;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
memcpy(path->value + path->len, temp.value, 2);
|
|
|
|
path->len += 2;
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
file->id = (temp.value[0] << 8) | temp.value[1];
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_structure(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
|
|
|
unsigned int ef_structure;
|
|
|
|
|
2002-04-05 08:45:14 +00:00
|
|
|
if (map_str2int(cur, argv[0], &ef_structure, fileStructureNames))
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
2002-04-03 11:52:30 +00:00
|
|
|
cur->file->file->ef_structure = ef_structure;
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_size(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
|
|
|
unsigned int size;
|
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
if (get_uint_eval(cur, argc, argv, &size))
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
2002-04-03 11:52:30 +00:00
|
|
|
cur->file->file->size = size;
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_reclength(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
|
|
|
unsigned int reclength;
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
if (get_uint(cur, argv[0], &reclength))
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
2002-04-03 11:52:30 +00:00
|
|
|
cur->file->file->record_length = reclength;
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-14 19:54:10 +00:00
|
|
|
static int
|
|
|
|
do_content(struct state *cur, int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct sc_file *file = cur->file->file;
|
|
|
|
size_t len = (strlen(argv[0]) + 1) / 2;
|
|
|
|
int rv = 0;
|
|
|
|
|
|
|
|
file->encoded_content = malloc(len);
|
|
|
|
if (!file->encoded_content)
|
|
|
|
return 1;
|
|
|
|
rv = sc_hex_to_bin(argv[0], file->encoded_content, &len);
|
|
|
|
file->encoded_content_len = len;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
do_prop_attr(struct state *cur, int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct sc_file *file = cur->file->file;
|
|
|
|
size_t len = (strlen(argv[0]) + 1) / 2;
|
|
|
|
int rv = 0;
|
|
|
|
|
|
|
|
file->prop_attr = malloc(len);
|
|
|
|
if (!file->prop_attr)
|
|
|
|
return 1;
|
|
|
|
rv = sc_hex_to_bin(argv[0], file->prop_attr, &len);
|
|
|
|
file->prop_attr_len = len;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_aid(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
struct sc_file *file = cur->file->file;
|
2002-04-02 13:26:42 +00:00
|
|
|
const char *name = argv[0];
|
|
|
|
unsigned int len;
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
if (*name == '=') {
|
|
|
|
len = strlen(++name);
|
|
|
|
if (len > sizeof(file->name)) {
|
2002-04-03 11:52:30 +00:00
|
|
|
parse_error(cur, "AID \"%s\" too long\n", name);
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
memcpy(file->name, name, len);
|
|
|
|
file->namelen = len;
|
2012-04-02 21:40:05 +00:00
|
|
|
}
|
2011-01-17 16:05:43 +00:00
|
|
|
else {
|
2002-04-02 13:26:42 +00:00
|
|
|
file->namelen = sizeof(file->name);
|
|
|
|
res = sc_hex_to_bin(name, file->name, &file->namelen);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2011-01-17 15:18:11 +00:00
|
|
|
static int
|
|
|
|
do_exclusive_aid(struct state *cur, int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct sc_file *file = cur->file->file;
|
|
|
|
const char *name = argv[0];
|
|
|
|
unsigned int len;
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
#ifdef DEBUG_PROFILE
|
|
|
|
printf("do_exclusive_aid(): exclusive-aid '%s'\n", name);
|
|
|
|
printf("do_exclusive_aid(): current file '%s' (path:%s)\n", cur->file->ident, sc_print_path(&file->path));
|
|
|
|
#endif
|
|
|
|
sc_format_path(name, &file->path);
|
|
|
|
if (file->path.len > SC_MAX_AID_SIZE) {
|
|
|
|
parse_error(cur, "Path length is too big\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(file->path.aid.value, file->path.value, file->path.len);
|
|
|
|
file->path.aid.len = file->path.len;
|
|
|
|
|
|
|
|
file->path.len = 0;
|
|
|
|
file->path.type = SC_PATH_TYPE_DF_NAME;
|
|
|
|
|
|
|
|
#ifdef DEBUG_PROFILE
|
|
|
|
printf("do_exclusive_aid(): '%s' exclusive-aid path %s\n", cur->file->ident, sc_print_path(&file->path));
|
|
|
|
#endif
|
|
|
|
if (*name == '=') {
|
|
|
|
len = strlen(++name);
|
|
|
|
if (len > sizeof(file->name)) {
|
|
|
|
parse_error(cur, "AID \"%s\" too long\n", name);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
memcpy(file->name, name, len);
|
|
|
|
file->namelen = len;
|
2012-04-02 21:40:05 +00:00
|
|
|
}
|
2011-01-17 15:18:11 +00:00
|
|
|
else {
|
|
|
|
file->namelen = sizeof(file->name);
|
|
|
|
res = sc_hex_to_bin(name, file->name, &file->namelen);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2011-03-20 12:18:55 +00:00
|
|
|
do_profile_extension(struct state *cur, int argc, char **argv)
|
2011-01-17 15:18:11 +00:00
|
|
|
{
|
2011-03-20 12:18:55 +00:00
|
|
|
return setstr(&cur->file->profile_extension, argv[0]);
|
2011-01-17 15:18:11 +00:00
|
|
|
}
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
/*
|
|
|
|
* Parse ACL list.
|
|
|
|
* The way we do this is we first split things like CHV1
|
|
|
|
* into a method (SC_AC_CHV) and a reference (1).
|
|
|
|
* When we're finished parsing the profile, the fake references
|
|
|
|
* are replaced by the real references given in KEY or PIN
|
|
|
|
* commands
|
|
|
|
*/
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_acl(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
struct sc_file *file = cur->file->file;
|
2007-06-21 11:07:00 +00:00
|
|
|
char oper[64], *what = NULL;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
memset(oper, 0, sizeof(oper));
|
2002-04-02 13:26:42 +00:00
|
|
|
while (argc--) {
|
|
|
|
unsigned int op, method, id;
|
|
|
|
|
2006-07-12 08:12:38 +00:00
|
|
|
strlcpy(oper, *argv++, sizeof(oper));
|
2002-04-02 13:26:42 +00:00
|
|
|
if ((what = strchr(oper, '=')) == NULL)
|
|
|
|
goto bad;
|
|
|
|
*what++ = '\0';
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
if (*what == '$') {
|
2002-04-02 13:26:42 +00:00
|
|
|
method = SC_AC_SYMBOLIC;
|
2002-04-03 11:52:30 +00:00
|
|
|
if (map_str2int(cur, what+1, &id, pinIdNames))
|
|
|
|
return 1;
|
2013-07-08 17:08:56 +00:00
|
|
|
}
|
|
|
|
else if (get_authid(cur, what, &method, &id))
|
2002-04-02 13:26:42 +00:00
|
|
|
goto bad;
|
|
|
|
|
2013-07-08 17:08:56 +00:00
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
if (!strcmp(oper, "*")) {
|
|
|
|
for (op = 0; op < SC_MAX_AC_OPS; op++) {
|
|
|
|
sc_file_clear_acl_entries(file, op);
|
|
|
|
sc_file_add_acl_entry(file, op, method, id);
|
|
|
|
}
|
|
|
|
} else {
|
2005-03-09 00:04:44 +00:00
|
|
|
const sc_acl_entry_t *acl;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
if (map_str2int(cur, oper, &op, fileOpNames))
|
2002-04-02 13:26:42 +00:00
|
|
|
goto bad;
|
|
|
|
acl = sc_file_get_acl_entry(file, op);
|
|
|
|
if (acl->method == SC_AC_NEVER
|
|
|
|
|| acl->method == SC_AC_NONE
|
|
|
|
|| acl->method == SC_AC_UNKNOWN)
|
|
|
|
sc_file_clear_acl_entries(file, op);
|
2013-07-08 17:08:56 +00:00
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
sc_file_add_acl_entry(file, op, method, id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
2012-04-02 21:40:05 +00:00
|
|
|
bad: parse_error(cur,
|
2002-04-02 13:26:42 +00:00
|
|
|
"Invalid ACL \"%s%s%s\"\n",
|
|
|
|
oper, what? "=" : "", what? what : "");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
process_pin(struct state *cur, struct block *info,
|
|
|
|
const char *name, scconf_block *blk)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
struct state state;
|
2002-04-02 13:26:42 +00:00
|
|
|
unsigned int id;
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
if (map_str2int(cur, name, &id, pinIdNames))
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
2002-04-03 11:52:30 +00:00
|
|
|
|
|
|
|
init_state(cur, &state);
|
2010-03-05 10:37:11 +00:00
|
|
|
state.pin = new_pin(cur->profile, (int)id);
|
2002-04-03 11:52:30 +00:00
|
|
|
|
|
|
|
return process_block(&state, info, name, blk);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pin_info *
|
2010-03-05 10:37:11 +00:00
|
|
|
new_pin(struct sc_profile *profile, int id)
|
2002-04-03 11:52:30 +00:00
|
|
|
{
|
|
|
|
struct pin_info *pi, **tail;
|
|
|
|
|
|
|
|
for (tail = &profile->pin_list; (pi = *tail); tail = &pi->next) {
|
|
|
|
if (pi->id == id)
|
|
|
|
return pi;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
2003-04-11 10:32:15 +00:00
|
|
|
/* Create pin info object. Most values are
|
|
|
|
* set to their defaults in set_pin_defaults later
|
|
|
|
* We can't do this here because these pin info objects
|
|
|
|
* are usually created before we've read the card specific
|
|
|
|
* profile
|
|
|
|
*/
|
Do not cast the return value of malloc(3) and calloc(3)
From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety
" Casting and type safety
malloc returns a void pointer (void *), which indicates that it is a
pointer to a region of unknown data type. One may "cast" (see type
conversion) this pointer to a specific type, as in
int *ptr = (int*)malloc(10 * sizeof (int));
When using C, this is considered bad practice; it is redundant under the
C standard. Moreover, putting in a cast may mask failure to include the
header stdlib.h, in which the prototype for malloc is found. In the
absence of a prototype for malloc, the C compiler will assume that
malloc returns an int, and will issue a warning in a context such as the
above, provided the error is not masked by a cast. On certain
architectures and data models (such as LP64 on 64 bit systems, where
long and pointers are 64 bit and int is 32 bit), this error can actually
result in undefined behavior, as the implicitly declared malloc returns
a 32 bit value whereas the actually defined function returns a 64 bit
value. Depending on calling conventions and memory layout, this may
result in stack smashing.
The returned pointer need not be explicitly cast to a more specific
pointer type, since ANSI C defines an implicit conversion between the
void pointer type and other pointers to objects. An explicit cast of
malloc's return value is sometimes performed because malloc originally
returned a char *, but this cast is unnecessary in standard C
code.[4][5] Omitting the cast, however, creates an incompatibility with
C++, which does require it.
The lack of a specific pointer type returned from malloc is type-unsafe
behaviour: malloc allocates based on byte count but not on type. This
distinguishes it from the C++ new operator that returns a pointer whose
type relies on the operand. (see C Type Safety). "
See also
http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
|
|
|
pi = calloc(1, sizeof(*pi));
|
2005-01-03 17:25:18 +00:00
|
|
|
if (pi == NULL)
|
|
|
|
return NULL;
|
2002-04-03 11:52:30 +00:00
|
|
|
pi->id = id;
|
2011-06-05 15:46:25 +00:00
|
|
|
pi->pin.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;
|
2010-02-20 22:04:07 +00:00
|
|
|
pi->pin.auth_method = SC_AC_CHV;
|
2011-06-05 15:46:25 +00:00
|
|
|
pi->pin.attrs.pin.type = (unsigned int)-1;
|
|
|
|
pi->pin.attrs.pin.flags = 0x32;
|
|
|
|
pi->pin.attrs.pin.max_length = 0;
|
|
|
|
pi->pin.attrs.pin.min_length = 0;
|
|
|
|
pi->pin.attrs.pin.stored_length = 0;
|
|
|
|
pi->pin.attrs.pin.pad_char = 0xA5;
|
|
|
|
pi->pin.attrs.pin.reference = -1;
|
2002-04-03 11:52:30 +00:00
|
|
|
pi->pin.tries_left = 3;
|
|
|
|
|
|
|
|
*tail = pi;
|
|
|
|
return pi;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
2007-06-21 11:07:00 +00:00
|
|
|
static void set_pin_defaults(struct sc_profile *profile, struct pin_info *pi)
|
2003-04-11 10:32:15 +00:00
|
|
|
{
|
2011-06-05 15:46:25 +00:00
|
|
|
struct sc_pkcs15_auth_info *info = &pi->pin;
|
|
|
|
struct sc_pkcs15_pin_attributes *pin_attrs = &info->attrs.pin;
|
|
|
|
|
|
|
|
info->auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;
|
2003-04-11 10:32:15 +00:00
|
|
|
|
2011-06-05 15:46:25 +00:00
|
|
|
if (pin_attrs->type == (unsigned int) -1)
|
|
|
|
pin_attrs->type = profile->pin_encoding;
|
|
|
|
if (pin_attrs->max_length == 0)
|
|
|
|
pin_attrs->max_length = profile->pin_maxlen;
|
|
|
|
if (pin_attrs->min_length == 0)
|
|
|
|
pin_attrs->min_length = profile->pin_minlen;
|
|
|
|
if (pin_attrs->stored_length == 0) {
|
|
|
|
pin_attrs->stored_length = profile->pin_maxlen;
|
2003-04-11 10:32:15 +00:00
|
|
|
/* BCD encoded PIN takes half the space */
|
2011-06-05 15:46:25 +00:00
|
|
|
if (pin_attrs->type == SC_PKCS15_PIN_TYPE_BCD)
|
|
|
|
pin_attrs->stored_length = (pin_attrs->stored_length + 1) / 2;
|
2003-04-11 10:32:15 +00:00
|
|
|
}
|
2011-06-05 15:46:25 +00:00
|
|
|
if (pin_attrs->pad_char == 0xA5)
|
|
|
|
pin_attrs->pad_char = profile->pin_pad_char;
|
2003-04-11 10:32:15 +00:00
|
|
|
}
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_pin_file(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-08 09:29:15 +00:00
|
|
|
cur->pin->file_name = strdup(argv[0]);
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_pin_offset(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
return get_uint(cur, argv[0], &cur->pin->file_offset);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_pin_attempts(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
struct pin_info *pi = cur->pin;
|
|
|
|
unsigned int count;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
if (get_uint(cur, argv[0], &count))
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
2002-04-03 11:52:30 +00:00
|
|
|
pi->pin.tries_left = count;
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-25 22:08:03 +00:00
|
|
|
static int
|
|
|
|
do_pin_maxunlocks(struct state *cur, int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct pin_info *pi = cur->pin;
|
|
|
|
unsigned int count;
|
|
|
|
|
|
|
|
if (get_uint(cur, argv[0], &count))
|
|
|
|
return 1;
|
|
|
|
pi->pin.max_unlocks = count;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_pin_type(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
|
|
|
unsigned int type;
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
if (map_str2int(cur, argv[0], &type, pinTypeNames))
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
2011-06-05 15:46:25 +00:00
|
|
|
if (cur->pin->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
|
|
|
|
return 1;
|
|
|
|
cur->pin->pin.attrs.pin.type = type;
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_pin_reference(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
|
|
|
unsigned int reference;
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
if (get_uint(cur, argv[0], &reference))
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
2011-06-05 15:46:25 +00:00
|
|
|
if (cur->pin->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
|
|
|
|
return 1;
|
|
|
|
cur->pin->pin.attrs.pin.reference = reference;
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_pin_authid(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
sc_pkcs15_format_id(argv[0], &cur->pin->pin.auth_id);
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_pin_minlength(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
unsigned int len;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
if (get_uint(cur, argv[0], &len))
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
2011-06-05 15:46:25 +00:00
|
|
|
if (cur->pin->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
|
|
|
|
return 1;
|
|
|
|
cur->pin->pin.attrs.pin.min_length = len;
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
do_pin_maxlength(struct state *cur, int argc, char **argv)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
unsigned int len;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2004-02-03 10:25:20 +00:00
|
|
|
if (get_uint(cur, argv[0], &len))
|
|
|
|
return 1;
|
2011-06-05 15:46:25 +00:00
|
|
|
if (cur->pin->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
|
|
|
|
return 1;
|
|
|
|
cur->pin->pin.attrs.pin.max_length = len;
|
2004-02-03 10:25:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
do_pin_storedlength(struct state *cur, int argc, char **argv)
|
|
|
|
{
|
|
|
|
unsigned int len;
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
if (get_uint(cur, argv[0], &len))
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
2011-06-05 15:46:25 +00:00
|
|
|
if (cur->pin->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
|
|
|
|
return 1;
|
|
|
|
cur->pin->pin.attrs.pin.stored_length = len;
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-04-07 13:15:31 +00:00
|
|
|
static int
|
|
|
|
do_pin_flags(struct state *cur, int argc, char **argv)
|
|
|
|
{
|
|
|
|
unsigned int flags;
|
2003-10-13 16:13:12 +00:00
|
|
|
int i, r;
|
|
|
|
|
2011-06-05 15:46:25 +00:00
|
|
|
if (cur->pin->pin.auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
cur->pin->pin.attrs.pin.flags = 0;
|
2003-10-13 16:13:12 +00:00
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
if ((r = map_str2int(cur, argv[i], &flags, pinFlagNames)) < 0)
|
|
|
|
return r;
|
2011-06-05 15:46:25 +00:00
|
|
|
cur->pin->pin.attrs.pin.flags |= flags;
|
2003-10-13 16:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
process_macros(struct state *cur, struct block *info,
|
|
|
|
const char *dummy, scconf_block *blk)
|
|
|
|
{
|
|
|
|
scconf_item *item;
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
for (item = blk->items; item; item = item->next) {
|
|
|
|
name = item->key;
|
|
|
|
if (item->type != SCCONF_ITEM_TYPE_VALUE)
|
|
|
|
continue;
|
2010-01-16 20:55:45 +00:00
|
|
|
#ifdef DEBUG_PROFILE
|
2003-10-13 16:13:12 +00:00
|
|
|
printf("Defining %s\n", name);
|
|
|
|
#endif
|
|
|
|
new_macro(cur->profile, name, item->value.list);
|
|
|
|
}
|
2002-04-07 13:15:31 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
static void
|
|
|
|
new_macro(sc_profile_t *profile, const char *name, scconf_list *value)
|
|
|
|
{
|
|
|
|
sc_macro_t *mac;
|
|
|
|
|
|
|
|
if ((mac = find_macro(profile, name)) == NULL) {
|
Do not cast the return value of malloc(3) and calloc(3)
From http://en.wikipedia.org/wiki/Malloc#Casting_and_type_safety
" Casting and type safety
malloc returns a void pointer (void *), which indicates that it is a
pointer to a region of unknown data type. One may "cast" (see type
conversion) this pointer to a specific type, as in
int *ptr = (int*)malloc(10 * sizeof (int));
When using C, this is considered bad practice; it is redundant under the
C standard. Moreover, putting in a cast may mask failure to include the
header stdlib.h, in which the prototype for malloc is found. In the
absence of a prototype for malloc, the C compiler will assume that
malloc returns an int, and will issue a warning in a context such as the
above, provided the error is not masked by a cast. On certain
architectures and data models (such as LP64 on 64 bit systems, where
long and pointers are 64 bit and int is 32 bit), this error can actually
result in undefined behavior, as the implicitly declared malloc returns
a 32 bit value whereas the actually defined function returns a 64 bit
value. Depending on calling conventions and memory layout, this may
result in stack smashing.
The returned pointer need not be explicitly cast to a more specific
pointer type, since ANSI C defines an implicit conversion between the
void pointer type and other pointers to objects. An explicit cast of
malloc's return value is sometimes performed because malloc originally
returned a char *, but this cast is unnecessary in standard C
code.[4][5] Omitting the cast, however, creates an incompatibility with
C++, which does require it.
The lack of a specific pointer type returned from malloc is type-unsafe
behaviour: malloc allocates based on byte count but not on type. This
distinguishes it from the C++ new operator that returns a pointer whose
type relies on the operand. (see C Type Safety). "
See also
http://www.opensc-project.org/pipermail/opensc-devel/2010-August/014586.html
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@4636 c6295689-39f2-0310-b995-f0e70906c6a9
2010-08-18 15:08:51 +00:00
|
|
|
mac = calloc(1, sizeof(*mac));
|
2005-01-03 17:25:18 +00:00
|
|
|
if (mac == NULL)
|
|
|
|
return;
|
2003-10-13 16:13:12 +00:00
|
|
|
mac->name = strdup(name);
|
|
|
|
mac->next = profile->macro_list;
|
|
|
|
profile->macro_list = mac;
|
|
|
|
}
|
|
|
|
|
|
|
|
mac->value = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static sc_macro_t *
|
|
|
|
find_macro(sc_profile_t *profile, const char *name)
|
|
|
|
{
|
|
|
|
sc_macro_t *mac;
|
|
|
|
|
|
|
|
for (mac = profile->macro_list; mac; mac = mac->next) {
|
|
|
|
if (!strcmp(mac->name, name))
|
|
|
|
return mac;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
/*
|
|
|
|
* Key section
|
|
|
|
*/
|
|
|
|
static struct command key_commands[] = {
|
|
|
|
{ "value", 1, 1, do_key_value },
|
2005-08-19 17:56:56 +00:00
|
|
|
{ NULL, 0, 0, NULL }
|
2002-04-03 11:52:30 +00:00
|
|
|
};
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
/*
|
|
|
|
* Cardinfo section
|
|
|
|
*/
|
|
|
|
static struct command ci_commands[] = {
|
|
|
|
{ "driver", 1, 1, do_card_driver },
|
|
|
|
{ "max-pin-length", 1, 1, do_maxpinlength },
|
|
|
|
{ "min-pin-length", 1, 1, do_minpinlength },
|
|
|
|
{ "pin-encoding", 1, 1, do_default_pin_type },
|
|
|
|
{ "pin-pad-char", 1, 1, do_pin_pad_char },
|
2003-10-13 16:13:12 +00:00
|
|
|
{ "pin-domains", 1, 1, do_pin_domains },
|
2002-04-03 11:52:30 +00:00
|
|
|
{ "label", 1, 1, do_card_label },
|
|
|
|
{ "manufacturer", 1, 1, do_card_manufacturer},
|
|
|
|
|
|
|
|
{ NULL, 0, 0, NULL }
|
|
|
|
};
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
static struct block ci_blocks[] = {
|
|
|
|
{ "key", process_key, key_commands, NULL },
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2005-08-19 17:56:56 +00:00
|
|
|
{ NULL, NULL, NULL, NULL }
|
2002-04-03 11:52:30 +00:00
|
|
|
};
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
/*
|
|
|
|
* Filesystem section
|
|
|
|
*/
|
|
|
|
static struct command fs_commands[] = {
|
|
|
|
{ "type", 1, 1, do_file_type },
|
|
|
|
{ "path", 1, 1, do_file_path },
|
|
|
|
{ "file-id", 1, 1, do_fileid },
|
|
|
|
{ "structure", 1, 1, do_structure },
|
2003-10-13 16:13:12 +00:00
|
|
|
{ "size", 1, -1, do_size },
|
2002-04-03 11:52:30 +00:00
|
|
|
{ "record-length", 1, 1, do_reclength },
|
|
|
|
{ "AID", 1, 1, do_aid },
|
|
|
|
{ "ACL", 1, -1, do_acl },
|
2011-01-17 15:18:11 +00:00
|
|
|
/* AID dependent sub-profile */
|
2011-03-20 12:18:55 +00:00
|
|
|
{ "profile-extension", 1, 1, do_profile_extension },
|
2011-01-17 15:18:11 +00:00
|
|
|
/* AID of the DFs without file-id */
|
|
|
|
{ "exclusive-aid", 1, 1, do_exclusive_aid },
|
2013-07-14 19:54:10 +00:00
|
|
|
{ "content", 1, 1, do_content },
|
|
|
|
{ "prop-attr", 1, 1, do_prop_attr },
|
2002-04-03 11:52:30 +00:00
|
|
|
|
|
|
|
{ NULL, 0, 0, NULL }
|
|
|
|
};
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
static struct block fs_blocks[] = {
|
|
|
|
{ "DF", process_df, fs_commands, fs_blocks },
|
|
|
|
{ "EF", process_ef, fs_commands, fs_blocks },
|
2010-01-21 09:41:40 +00:00
|
|
|
{ "BSO", process_bso, fs_commands, fs_blocks },
|
2003-10-13 16:13:12 +00:00
|
|
|
{ "template", process_tmpl, fs_commands, fs_blocks },
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
{ NULL, NULL, NULL, NULL }
|
|
|
|
};
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
/*
|
|
|
|
* Pin section
|
|
|
|
*/
|
|
|
|
static struct command pi_commands[] = {
|
2013-12-25 22:08:03 +00:00
|
|
|
{ "file", 1, 1, do_pin_file },
|
|
|
|
{ "offset", 1, 1, do_pin_offset },
|
|
|
|
{ "attempts", 1, 2, do_pin_attempts },
|
|
|
|
{ "encoding", 1, 1, do_pin_type },
|
|
|
|
{ "reference", 1, 1, do_pin_reference },
|
|
|
|
{ "auth-id", 1, 1, do_pin_authid },
|
|
|
|
{ "max-length", 1, 1, do_pin_maxlength },
|
|
|
|
{ "min-length", 1, 1, do_pin_minlength },
|
|
|
|
{ "stored-length", 1, 1, do_pin_storedlength },
|
|
|
|
{ "max-unlocks", 1, 1, do_pin_maxunlocks },
|
|
|
|
{ "flags", 1, -1, do_pin_flags },
|
2002-04-03 11:52:30 +00:00
|
|
|
{ NULL, 0, 0, NULL }
|
|
|
|
};
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2003-11-19 20:37:02 +00:00
|
|
|
/*
|
|
|
|
* pkcs15 dialect section
|
|
|
|
*/
|
|
|
|
static struct command p15_commands[] = {
|
2012-05-26 07:17:21 +00:00
|
|
|
{ "direct-certificates", 1, 1, do_direct_certificates },
|
|
|
|
{ "encode-df-length", 1, 1, do_encode_df_length },
|
|
|
|
{ "do-last-update", 1, 1, do_encode_update_field },
|
|
|
|
{ "pkcs15-id-style", 1, 1, do_pkcs15_id_style },
|
|
|
|
{ "minidriver-support-style", 1, 1, do_minidriver_support_style },
|
2003-11-19 20:37:02 +00:00
|
|
|
{ NULL, 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
static struct block root_blocks[] = {
|
|
|
|
{ "filesystem", process_block, NULL, fs_blocks },
|
|
|
|
{ "cardinfo", process_block, ci_commands, ci_blocks },
|
|
|
|
{ "pin", process_pin, pi_commands, NULL },
|
2003-10-13 16:13:12 +00:00
|
|
|
{ "option", process_option, NULL, root_blocks },
|
|
|
|
{ "macros", process_macros, NULL, NULL },
|
2003-11-19 20:37:02 +00:00
|
|
|
{ "pkcs15", process_block, p15_commands, NULL },
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2005-08-19 17:56:56 +00:00
|
|
|
{ NULL, NULL, NULL, NULL }
|
2002-04-03 11:52:30 +00:00
|
|
|
};
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
static struct block root_ops = {
|
|
|
|
"root", process_block, NULL, root_blocks
|
|
|
|
};
|
2002-04-02 13:26:42 +00:00
|
|
|
|
|
|
|
static int
|
2003-10-13 16:13:12 +00:00
|
|
|
build_argv(struct state *cur, const char *cmdname,
|
|
|
|
scconf_list *list, char **argv, unsigned int max)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2003-10-13 16:13:12 +00:00
|
|
|
unsigned int argc;
|
|
|
|
const char *str;
|
|
|
|
sc_macro_t *mac;
|
|
|
|
int r;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
for (argc = 0; list; list = list->next) {
|
2003-10-13 16:13:12 +00:00
|
|
|
if (argc >= max) {
|
|
|
|
parse_error(cur, "%s: too many arguments", cmdname);
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
str = list->data;
|
|
|
|
if (str[0] != '$') {
|
|
|
|
argv[argc++] = list->data;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Expand macro reference */
|
|
|
|
if (!(mac = find_macro(cur->profile, str + 1))) {
|
|
|
|
parse_error(cur, "%s: unknown macro \"%s\"",
|
|
|
|
cmdname, str);
|
|
|
|
return SC_ERROR_SYNTAX_ERROR;
|
|
|
|
}
|
|
|
|
|
2010-01-16 20:55:45 +00:00
|
|
|
#ifdef DEBUG_PROFILE
|
2003-10-13 16:13:12 +00:00
|
|
|
{
|
|
|
|
scconf_list *list;
|
|
|
|
|
|
|
|
printf("Expanding macro %s:", mac->name);
|
|
|
|
for (list = mac->value; list; list = list->next)
|
|
|
|
printf(" %s", list->data);
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
r = build_argv(cur, cmdname, mac->value,
|
|
|
|
argv + argc, max - argc);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
argc += r;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
return argc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
process_command(struct state *cur, struct command *cmd_info, scconf_list *list)
|
|
|
|
{
|
|
|
|
const char *cmd = cmd_info->name;
|
|
|
|
char *argv[32];
|
|
|
|
int argc, max = 32;
|
|
|
|
|
|
|
|
if (cmd_info->max_args >= 0 && max > cmd_info->max_args)
|
|
|
|
max = cmd_info->max_args;
|
|
|
|
|
|
|
|
if ((argc = build_argv(cur, cmd, list, argv, max)) < 0)
|
|
|
|
return argc;
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
if (argc < cmd_info->min_args) {
|
|
|
|
parse_error(cur, "%s: not enough arguments\n", cmd);
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
2002-04-03 11:52:30 +00:00
|
|
|
}
|
|
|
|
return cmd_info->func(cur, argc, argv);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
static struct block *
|
|
|
|
find_block_handler(struct block *bp, const char *name)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
if (bp == NULL)
|
|
|
|
return NULL;
|
|
|
|
for (; bp->name; bp++) {
|
|
|
|
if (!strcasecmp(bp->name, name))
|
|
|
|
return bp;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
2002-04-03 11:52:30 +00:00
|
|
|
return NULL;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
static struct command *
|
|
|
|
find_cmd_handler(struct command *cp, const char *name)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
if (cp == NULL)
|
|
|
|
return NULL;
|
|
|
|
for (; cp->name; cp++) {
|
|
|
|
if (!strcasecmp(cp->name, name))
|
|
|
|
return cp;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
2002-04-03 11:52:30 +00:00
|
|
|
return NULL;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
process_block(struct state *cur, struct block *info,
|
|
|
|
const char *name, scconf_block *blk)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
scconf_item *item;
|
|
|
|
struct command *cp;
|
|
|
|
struct block *bp;
|
|
|
|
const char *cmd, *ident;
|
|
|
|
int res = 0;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
for (item = blk->items; res == 0 && item; item = item->next) {
|
|
|
|
cmd = item->key;
|
|
|
|
if (item->type == SCCONF_ITEM_TYPE_COMMENT)
|
|
|
|
continue;
|
|
|
|
if (item->type == SCCONF_ITEM_TYPE_BLOCK) {
|
|
|
|
scconf_list *nlist;
|
|
|
|
|
|
|
|
ident = NULL;
|
|
|
|
if ((nlist = item->value.block->name) != NULL) {
|
|
|
|
if (nlist->next) {
|
2011-01-17 14:03:01 +00:00
|
|
|
parse_error(cur, "Too many name components in block name.");
|
2002-04-03 11:52:30 +00:00
|
|
|
return SC_ERROR_SYNTAX_ERROR;
|
|
|
|
}
|
|
|
|
ident = nlist->data;
|
|
|
|
}
|
2010-01-16 20:55:45 +00:00
|
|
|
#ifdef DEBUG_PROFILE
|
2011-01-17 14:03:01 +00:00
|
|
|
printf("Processing %s %s\n", cmd, ident? ident : "");
|
2002-04-02 13:26:42 +00:00
|
|
|
#endif
|
2002-04-03 11:52:30 +00:00
|
|
|
if ((bp = find_block_handler(info->blk_info, cmd))) {
|
2011-01-17 14:03:01 +00:00
|
|
|
res = bp->handler(cur, bp, ident, item->value.block);
|
2002-04-03 11:52:30 +00:00
|
|
|
continue;
|
|
|
|
}
|
2011-01-17 14:03:01 +00:00
|
|
|
}
|
|
|
|
else if (item->type == SCCONF_ITEM_TYPE_VALUE) {
|
2010-01-16 20:55:45 +00:00
|
|
|
#ifdef DEBUG_PROFILE
|
2003-10-13 16:13:12 +00:00
|
|
|
printf("Processing %s\n", cmd);
|
|
|
|
#endif
|
2002-04-03 11:52:30 +00:00
|
|
|
if ((cp = find_cmd_handler(info->cmd_info, cmd))) {
|
2011-01-17 14:03:01 +00:00
|
|
|
res = process_command(cur, cp, item->value.list);
|
2002-04-03 11:52:30 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2011-01-17 14:03:01 +00:00
|
|
|
parse_error(cur, "Command \"%s\" not understood in this context.", cmd);
|
2002-04-03 11:52:30 +00:00
|
|
|
return SC_ERROR_SYNTAX_ERROR;
|
|
|
|
}
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
if (res > 0)
|
|
|
|
res = SC_ERROR_SYNTAX_ERROR;
|
|
|
|
return res;
|
|
|
|
}
|
2002-04-02 13:26:42 +00:00
|
|
|
|
|
|
|
static int
|
2003-11-19 20:37:02 +00:00
|
|
|
process_conf(struct sc_profile *profile, scconf_context *conf)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-03 11:52:30 +00:00
|
|
|
struct state state;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
memset(&state, 0, sizeof(state));
|
|
|
|
state.filename = conf->filename;
|
|
|
|
state.profile = profile;
|
|
|
|
return process_block(&state, &root_ops, "root", conf->root);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct file_info *
|
2003-10-13 16:13:12 +00:00
|
|
|
sc_profile_find_file(struct sc_profile *pro,
|
|
|
|
const sc_path_t *path, const char *name)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
|
|
|
struct file_info *fi;
|
2003-10-13 16:13:12 +00:00
|
|
|
unsigned int len;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
len = path? path->len : 0;
|
2002-04-02 13:26:42 +00:00
|
|
|
for (fi = pro->ef_list; fi; fi = fi->next) {
|
2003-10-13 16:13:12 +00:00
|
|
|
sc_path_t *fpath = &fi->file->path;
|
|
|
|
|
2012-05-26 07:17:21 +00:00
|
|
|
if (!strcasecmp(fi->ident, name) && fpath->len >= len && !memcmp(fpath->value, path->value, len))
|
2002-04-02 13:26:42 +00:00
|
|
|
return fi;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-01-17 14:03:01 +00:00
|
|
|
|
2010-01-27 18:18:10 +00:00
|
|
|
static struct file_info *
|
|
|
|
sc_profile_find_file_by_path(struct sc_profile *pro, const sc_path_t *path)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2010-03-04 12:55:09 +00:00
|
|
|
struct file_info *fi, *out = NULL;
|
2011-01-17 16:05:43 +00:00
|
|
|
struct sc_path *fp_path, *fpp_path;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2010-03-04 12:55:09 +00:00
|
|
|
#ifdef DEBUG_PROFILE
|
2011-01-19 14:16:12 +00:00
|
|
|
struct sc_context *ctx = pro->card->ctx;
|
|
|
|
|
2011-01-17 16:05:43 +00:00
|
|
|
sc_log(ctx, "profile's EF list:");
|
|
|
|
for (fi = pro->ef_list; fi; fi = fi->next) {
|
|
|
|
sc_log(ctx, "'%s' (path:%s)", fi->ident, sc_print_path(&fi->file->path));
|
|
|
|
sc_log(ctx, "fi parent %p", fi->parent);
|
|
|
|
if (fi->parent && fi->parent->file)
|
|
|
|
sc_log(ctx, "fi parent path %s", sc_print_path(&fi->parent->file->path));
|
|
|
|
}
|
|
|
|
sc_log(ctx, "find profile file by path:%s", sc_print_path(path));
|
2010-03-04 12:55:09 +00:00
|
|
|
#endif
|
2011-01-17 16:05:43 +00:00
|
|
|
|
2015-01-22 19:29:33 +00:00
|
|
|
if (!path || (!path->len && !path->aid.len))
|
2011-01-17 16:05:43 +00:00
|
|
|
return NULL;
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
for (fi = pro->ef_list; fi; fi = fi->next) {
|
2011-01-17 16:05:43 +00:00
|
|
|
fp_path = &fi->file->path;
|
|
|
|
fpp_path = fi->parent ? &fi->parent->file->path : NULL;
|
|
|
|
|
|
|
|
if (fp_path->len != path->len)
|
|
|
|
continue;
|
|
|
|
if (fp_path->len && memcmp(fp_path->value, path->value, path->len))
|
|
|
|
continue;
|
2012-04-02 21:40:05 +00:00
|
|
|
|
2011-01-17 16:05:43 +00:00
|
|
|
if (path->aid.len && fp_path->aid.len) {
|
|
|
|
if (memcmp(fp_path->aid.value, path->aid.value, path->aid.len))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (path->aid.len && !fp_path->aid.len && fpp_path) {
|
|
|
|
if (fpp_path->type == SC_PATH_TYPE_DF_NAME && fpp_path->len) {
|
|
|
|
if (fpp_path->len != path->aid.len)
|
|
|
|
continue;
|
|
|
|
if (memcmp(fpp_path->value, path->aid.value, path->aid.len))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (fpp_path->aid.len) {
|
|
|
|
if (fpp_path->aid.len != path->aid.len)
|
|
|
|
continue;
|
|
|
|
if (memcmp(fpp_path->aid.value, path->aid.value, path->aid.len))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out = fi;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
2010-03-04 12:55:09 +00:00
|
|
|
|
|
|
|
#ifdef DEBUG_PROFILE
|
2011-01-17 16:05:43 +00:00
|
|
|
sc_log(ctx, "returns (%s)", out ? out->ident: "<null>");
|
2010-03-04 12:55:09 +00:00
|
|
|
#endif
|
|
|
|
return out;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
2011-01-17 15:51:47 +00:00
|
|
|
int
|
|
|
|
sc_profile_get_parent(struct sc_profile *profile,
|
|
|
|
const char *name, sc_file_t **ret)
|
|
|
|
{
|
|
|
|
struct file_info *fi = NULL;
|
|
|
|
|
|
|
|
if ((fi = sc_profile_find_file(profile, NULL, name)) == NULL)
|
|
|
|
return SC_ERROR_FILE_NOT_FOUND;
|
|
|
|
|
|
|
|
if (!fi->parent)
|
|
|
|
return SC_ERROR_FILE_NOT_FOUND;
|
|
|
|
|
|
|
|
sc_file_dup(ret, fi->parent->file);
|
|
|
|
if (*ret == NULL)
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
/*
|
|
|
|
* Split up KEY0 or CHV1 into SC_AC_XXX and a number
|
|
|
|
*/
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
get_authid(struct state *cur, const char *value,
|
|
|
|
unsigned int *type, unsigned int *num)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
|
|
|
char temp[16];
|
2005-08-19 17:56:56 +00:00
|
|
|
size_t n;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
|
|
|
if (isdigit((int) *value)) {
|
|
|
|
*num = 0;
|
2002-04-03 11:52:30 +00:00
|
|
|
return get_uint(cur, value, type);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
2011-01-17 15:51:47 +00:00
|
|
|
n = strcspn(value, "0123456789x");
|
2006-07-14 08:18:57 +00:00
|
|
|
strlcpy(temp, value, (sizeof(temp) > n) ? n + 1 : sizeof(temp));
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2002-04-03 11:52:30 +00:00
|
|
|
if (map_str2int(cur, temp, type, aclNames))
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
|
|
|
if (value[n])
|
2002-04-03 11:52:30 +00:00
|
|
|
return get_uint(cur, value + n, num);
|
2002-04-02 13:26:42 +00:00
|
|
|
*num = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
get_uint(struct state *cur, const char *value, unsigned int *vp)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2004-12-22 09:48:27 +00:00
|
|
|
char *ep;
|
2002-04-02 13:26:42 +00:00
|
|
|
|
2010-01-21 09:41:40 +00:00
|
|
|
if (strstr(value, "0x") == value)
|
|
|
|
*vp = strtoul(value + 2, &ep, 16);
|
2011-01-17 15:51:47 +00:00
|
|
|
else if (strstr(value, "x") == value)
|
|
|
|
*vp = strtoul(value + 1, &ep, 16);
|
2010-01-21 09:41:40 +00:00
|
|
|
else
|
|
|
|
*vp = strtoul(value, &ep, 0);
|
2002-04-02 13:26:42 +00:00
|
|
|
if (*ep != '\0') {
|
2011-01-17 14:03:01 +00:00
|
|
|
parse_error(cur, "invalid integer argument \"%s\"\n", value);
|
2002-04-02 13:26:42 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
static int
|
|
|
|
get_bool(struct state *cur, const char *value, unsigned int *vp)
|
|
|
|
{
|
|
|
|
if (!strcasecmp(value, "on")
|
|
|
|
|| !strcasecmp(value, "yes")
|
|
|
|
|| !strcasecmp(value, "true")) {
|
|
|
|
*vp = 1;
|
|
|
|
} else
|
|
|
|
if (!strcasecmp(value, "off")
|
|
|
|
|| !strcasecmp(value, "no")
|
|
|
|
|| !strcasecmp(value, "false")) {
|
|
|
|
*vp = 0;
|
|
|
|
} else {
|
|
|
|
parse_error(cur, "invalid boolean argument \"%s\"\n", value);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
static int
|
2002-04-03 11:52:30 +00:00
|
|
|
map_str2int(struct state *cur, const char *value,
|
|
|
|
unsigned int *vp, struct map *map)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-05 08:45:14 +00:00
|
|
|
unsigned int n;
|
|
|
|
const char *what;
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
if (isdigit((int) *value))
|
2002-04-03 11:52:30 +00:00
|
|
|
return get_uint(cur, value, vp);
|
2002-04-05 08:45:14 +00:00
|
|
|
for (n = 0; map[n].name; n++) {
|
|
|
|
if (!strcasecmp(value, map[n].name)) {
|
|
|
|
*vp = map[n].val;
|
2002-04-02 13:26:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2002-04-05 08:45:14 +00:00
|
|
|
|
|
|
|
/* Try to print a meaningful error message */
|
|
|
|
what = "argument";
|
|
|
|
for (n = 0; mapNames[n].name; n++) {
|
|
|
|
if (mapNames[n].addr == map) {
|
|
|
|
what = mapNames[n].name;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
parse_error(cur, "invalid %s \"%s\"\n", what, value);
|
2003-10-13 16:13:12 +00:00
|
|
|
return SC_ERROR_SYNTAX_ERROR;
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
setstr(char **strp, const char *value)
|
|
|
|
{
|
|
|
|
if (*strp)
|
|
|
|
free(*strp);
|
|
|
|
*strp = strdup(value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-10-13 16:13:12 +00:00
|
|
|
/*
|
|
|
|
* Evaluate numeric expressions
|
|
|
|
*/
|
|
|
|
#include <setjmp.h>
|
|
|
|
|
|
|
|
struct num_exp_ctx {
|
|
|
|
struct state * state;
|
|
|
|
jmp_buf error;
|
|
|
|
|
|
|
|
int j;
|
|
|
|
char word[64];
|
|
|
|
|
|
|
|
char * unget;
|
|
|
|
char * str;
|
|
|
|
int argc;
|
|
|
|
char ** argv;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void expr_eval(struct num_exp_ctx *, unsigned int *, unsigned int);
|
|
|
|
|
|
|
|
static void
|
|
|
|
expr_fail(struct num_exp_ctx *ctx)
|
|
|
|
{
|
|
|
|
longjmp(ctx->error, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-08-19 17:56:56 +00:00
|
|
|
expr_put(struct num_exp_ctx *ctx, int c)
|
2003-10-13 16:13:12 +00:00
|
|
|
{
|
2004-12-22 09:48:27 +00:00
|
|
|
if (ctx->j >= (int)sizeof(ctx->word))
|
2003-10-13 16:13:12 +00:00
|
|
|
expr_fail(ctx);
|
2005-08-19 17:56:56 +00:00
|
|
|
ctx->word[ctx->j++] = (char)c;
|
2003-10-13 16:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
__expr_get(struct num_exp_ctx *ctx, int eof_okay)
|
|
|
|
{
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
if ((s = ctx->unget) != NULL) {
|
|
|
|
ctx->unget = NULL;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->j = 0;
|
|
|
|
do {
|
|
|
|
if ((s = ctx->str) == NULL || *s == '\0') {
|
|
|
|
if (ctx->argc == 0) {
|
|
|
|
if (eof_okay)
|
|
|
|
return NULL;
|
|
|
|
expr_fail(ctx);
|
|
|
|
}
|
|
|
|
ctx->str = s = *(ctx->argv++);
|
|
|
|
ctx->argc--;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (isspace(*s))
|
|
|
|
s++;
|
|
|
|
} while (*s == '\0');
|
|
|
|
|
|
|
|
if (isdigit(*s)) {
|
|
|
|
while (isdigit(*s))
|
|
|
|
expr_put(ctx, *s++);
|
2012-04-02 21:40:05 +00:00
|
|
|
}
|
2011-01-17 14:03:01 +00:00
|
|
|
else if (*s == '$') {
|
2003-10-13 16:13:12 +00:00
|
|
|
expr_put(ctx, *s++);
|
|
|
|
while (isalnum(*s) || *s == '-' || *s == '_')
|
|
|
|
expr_put(ctx, *s++);
|
2012-04-02 21:40:05 +00:00
|
|
|
}
|
2011-01-17 14:03:01 +00:00
|
|
|
else if (strchr("*/+-()|&", *s)) {
|
2003-10-13 16:13:12 +00:00
|
|
|
expr_put(ctx, *s++);
|
2012-04-02 21:40:05 +00:00
|
|
|
}
|
2011-01-17 14:03:01 +00:00
|
|
|
else {
|
2003-10-13 16:13:12 +00:00
|
|
|
expr_fail(ctx);
|
|
|
|
}
|
|
|
|
ctx->str = s;
|
|
|
|
|
|
|
|
expr_put(ctx, '\0');
|
|
|
|
return ctx->word;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
expr_get(struct num_exp_ctx *ctx)
|
|
|
|
{
|
|
|
|
return __expr_get(ctx, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
expr_unget(struct num_exp_ctx *ctx, char *s)
|
|
|
|
{
|
|
|
|
if (ctx->unget)
|
|
|
|
expr_fail(ctx);
|
|
|
|
ctx->unget = s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-08-19 17:56:56 +00:00
|
|
|
expr_expect(struct num_exp_ctx *ctx, int c)
|
2003-10-13 16:13:12 +00:00
|
|
|
{
|
|
|
|
char *tok;
|
|
|
|
|
|
|
|
tok = expr_get(ctx);
|
2005-08-19 17:56:56 +00:00
|
|
|
if (tok[0] != (char)c || tok[1])
|
2003-10-13 16:13:12 +00:00
|
|
|
expr_fail(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
expr_term(struct num_exp_ctx *ctx, unsigned int *vp)
|
|
|
|
{
|
|
|
|
char *tok;
|
|
|
|
|
|
|
|
tok = expr_get(ctx);
|
|
|
|
if (*tok == '(') {
|
|
|
|
expr_eval(ctx, vp, 1);
|
|
|
|
expr_expect(ctx, ')');
|
2012-04-02 21:40:05 +00:00
|
|
|
}
|
2011-01-17 14:03:01 +00:00
|
|
|
else if (isdigit(*tok)) {
|
2003-10-13 16:13:12 +00:00
|
|
|
char *ep;
|
|
|
|
|
|
|
|
*vp = strtoul(tok, &ep, 0);
|
|
|
|
if (*ep)
|
|
|
|
expr_fail(ctx);
|
2012-04-02 21:40:05 +00:00
|
|
|
}
|
2011-01-17 14:03:01 +00:00
|
|
|
else if (*tok == '$') {
|
2003-10-13 16:13:12 +00:00
|
|
|
sc_macro_t *mac;
|
|
|
|
char *argv[32];
|
|
|
|
int argc;
|
|
|
|
|
|
|
|
if (!(mac = find_macro(ctx->state->profile, tok + 1)))
|
|
|
|
expr_fail(ctx);
|
|
|
|
argc = build_argv(ctx->state, "<expr>", mac->value, argv, 32);
|
2011-01-17 14:03:01 +00:00
|
|
|
if (argc < 0 || get_uint_eval(ctx->state, argc, argv, vp) < 0)
|
2003-10-13 16:13:12 +00:00
|
|
|
expr_fail(ctx);
|
2012-04-02 21:40:05 +00:00
|
|
|
}
|
2011-01-17 14:03:01 +00:00
|
|
|
else {
|
|
|
|
parse_error(ctx->state, "Unexpected token \"%s\" in expression", tok);
|
2003-10-13 16:13:12 +00:00
|
|
|
expr_fail(ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
expr_eval(struct num_exp_ctx *ctx, unsigned int *vp, unsigned int pri)
|
|
|
|
{
|
|
|
|
unsigned int left, right, new_pri;
|
|
|
|
char *tok, op;
|
|
|
|
|
|
|
|
expr_term(ctx, &left);
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
tok = __expr_get(ctx, 1);
|
|
|
|
if (tok == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
op = tok[0];
|
|
|
|
|
|
|
|
new_pri = 0;
|
|
|
|
switch (op) {
|
|
|
|
case '*':
|
|
|
|
case '/':
|
|
|
|
new_pri++;
|
2015-10-09 09:09:57 +00:00
|
|
|
/* fall through */
|
2003-10-13 16:13:12 +00:00
|
|
|
case '+':
|
|
|
|
case '-':
|
|
|
|
new_pri++;
|
2015-10-09 09:09:57 +00:00
|
|
|
/* fall through */
|
2003-10-13 16:13:12 +00:00
|
|
|
case '&':
|
|
|
|
new_pri++;
|
2015-10-09 09:09:57 +00:00
|
|
|
/* fall through */
|
2003-10-13 16:13:12 +00:00
|
|
|
case '|':
|
|
|
|
new_pri++;
|
2015-10-09 09:09:57 +00:00
|
|
|
/* fall through */
|
2003-10-13 16:13:12 +00:00
|
|
|
case ')':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
expr_fail(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_pri < pri) {
|
|
|
|
expr_unget(ctx, tok);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
pri = new_pri;
|
|
|
|
|
|
|
|
expr_eval(ctx, &right, new_pri + 1);
|
|
|
|
switch (op) {
|
|
|
|
case '*': left *= right; break;
|
|
|
|
case '/': left /= right; break;
|
|
|
|
case '+': left += right; break;
|
|
|
|
case '-': left -= right; break;
|
|
|
|
case '&': left &= right; break;
|
|
|
|
case '|': left |= right; break;
|
|
|
|
default: expr_fail(ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*vp = left;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
get_uint_eval(struct state *cur, int argc, char **argv, unsigned int *vp)
|
|
|
|
{
|
|
|
|
struct num_exp_ctx ctx;
|
|
|
|
|
|
|
|
memset(&ctx, 0, sizeof(ctx));
|
|
|
|
ctx.state = cur;
|
|
|
|
ctx.argc = argc;
|
|
|
|
ctx.argv = argv;
|
|
|
|
|
|
|
|
if (setjmp(ctx.error)) {
|
|
|
|
parse_error(cur, "invalid numeric expression\n");
|
|
|
|
return SC_ERROR_SYNTAX_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
expr_eval(&ctx, vp, 0);
|
|
|
|
if (ctx.str[0] || ctx.argc)
|
|
|
|
expr_fail(&ctx);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-04-02 13:26:42 +00:00
|
|
|
static void
|
2002-04-03 11:52:30 +00:00
|
|
|
parse_error(struct state *cur, const char *fmt, ...)
|
2002-04-02 13:26:42 +00:00
|
|
|
{
|
2002-04-05 08:45:14 +00:00
|
|
|
char buffer[1024], *sp;
|
2002-04-02 13:26:42 +00:00
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
2002-04-03 11:52:30 +00:00
|
|
|
vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
2002-04-02 13:26:42 +00:00
|
|
|
va_end(ap);
|
|
|
|
|
2002-04-05 08:45:14 +00:00
|
|
|
if ((sp = strchr(buffer, '\n')) != NULL)
|
|
|
|
*sp = '\0';
|
|
|
|
|
2010-03-03 17:22:01 +00:00
|
|
|
if (cur->profile->card && cur->profile->card->ctx)
|
2011-01-17 16:52:04 +00:00
|
|
|
sc_log(cur->profile->card->ctx, "%s: %s", cur->filename, buffer);
|
2010-03-03 17:22:01 +00:00
|
|
|
else
|
|
|
|
fprintf(stdout, "%s: %s\n", cur->filename, buffer);
|
2002-04-02 13:26:42 +00:00
|
|
|
}
|