opensc/src/pkcs15init/profile.c

1260 lines
28 KiB
C
Raw Normal View History

/*
* Initialize Cards according to PKCS#15
*
* Copyright (C) 2002 Olaf Kirch <okir@lst.de>
*
* 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
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <opensc/scconf.h>
#include "pkcs15-init.h"
#include "profile.h"
#define DEF_PRKEY_RSA_ACCESS 0x1D
#define DEF_PRKEY_DSA_ACCESS 0x12
#define DEF_PUBKEY_ACCESS 0x12
/*
* 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;
};
struct command {
const char * name;
int min_args, max_args;
int (*func)(struct state *, int, char **);
};
struct block {
const char * name;
int (*handler)(struct state *,
struct block *,
const char *,
scconf_block *);
struct command * cmd_info;
struct block * blk_info;
};
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 },
{ 0, 0 }
};
static struct map fileOpNames[] = {
{ "SELECT", SC_AC_OP_SELECT },
{ "LOCK", SC_AC_OP_LOCK },
{ "DELETE", SC_AC_OP_DELETE },
{ "CREATE", SC_AC_OP_CREATE },
{ "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 },
{ "CRYPTO", SC_AC_OP_CRYPTO },
{ 0, 0 }
};
static struct map fileTypeNames[] = {
{ "EF", SC_FILE_TYPE_WORKING_EF },
{ "INTERNAL-EF",SC_FILE_TYPE_INTERNAL_EF },
{ "DF", SC_FILE_TYPE_DF },
{ 0, 0 }
};
static struct map fileStructureNames[] = {
{ "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 },
{ 0, 0 }
};
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 },
{ 0, 0 }
};
static struct map pinTypeNames[] = {
{ "BCD", 0 },
{ "ascii-numeric", 1 },
{ "utf8", 2 },
{ "half-nibble-bcd", 3 },
{ "iso9564-1", 4 },
{ 0, 0 }
};
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 },
{ 0, 0 }
};
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 },
{ NULL, NULL }
};
typedef struct pin_info pin_info;
typedef struct file_info file_info;
typedef struct auth_info auth_info;
static const char * sc_profile_locate(const char *);
static int process_conf(struct sc_profile *, scconf_context *);
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 *);
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 *, ...);
static file_info * sc_profile_find_file(struct sc_profile *,
const char *);
static file_info * sc_profile_find_file_by_path(
struct sc_profile *,
const struct sc_path *);
static pin_info * new_pin(struct sc_profile *, unsigned int);
static file_info * new_file(struct state *, const char *,
unsigned int);
static auth_info * new_key(struct sc_profile *,
unsigned int, unsigned int);
static struct sc_file *
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;
file->ef_structure = SC_FILE_EF_TRANSPARENT;
return file;
}
/*
* Initialize profile
*/
struct sc_profile *
sc_profile_new()
{
struct sc_pkcs15_card *p15card;
struct sc_profile *pro;
pro = (struct sc_profile *) calloc(1, sizeof(*pro));
pro->p15_card = p15card = sc_pkcs15_card_new();
/* 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);
if (p15card) {
p15card->label = strdup("OpenSC Card");
p15card->manufacturer_id = strdup("OpenSC Project");
p15card->serial_number = strdup("0000");
p15card->flags = SC_PKCS15_CARD_FLAG_EID_COMPLIANT;
p15card->version = 1;
}
/* Assume card does RSA natively, but no DSA */
pro->rsa_access_flags = DEF_PRKEY_RSA_ACCESS;
pro->dsa_access_flags = DEF_PRKEY_DSA_ACCESS;
pro->pin_encoding = 0x01;
pro->pin_minlen = 4;
pro->pin_maxlen = 8;
return pro;
}
int
sc_profile_load(struct sc_profile *profile, const char *filename)
{
scconf_context *conf;
int res = 0;
if (!(filename = sc_profile_locate(filename)))
return SC_ERROR_FILE_NOT_FOUND;
conf = scconf_new(filename);
res = scconf_parse(conf);
if (res < 0)
return SC_ERROR_FILE_NOT_FOUND;
if (res == 0)
return SC_ERROR_SYNTAX_ERROR;
res = process_conf(profile, conf);
scconf_free(conf);
return res;
}
int
sc_profile_finish(struct sc_profile *profile)
{
const char *reason;
reason = "Profile doesn't define a MF";
if (!(profile->mf_info = sc_profile_find_file(profile, "MF")))
goto whine;
reason = "Profile doesn't define a PKCS15-AppDF";
if (!(profile->df_info = sc_profile_find_file(profile, "PKCS15-AppDF")))
goto whine;
profile->p15_card->file_app = profile->df_info->file;
return 0;
whine: if (profile->cbs)
profile->cbs->error("%s\n", reason);
return SC_ERROR_INCONSISTENT_PROFILE;
}
void
sc_profile_free(struct sc_profile *profile)
{
/* XXX Needs to be done. Huge memory leak */
free(profile);
}
static const char *
sc_profile_locate(const char *name)
{
static char path[1024];
/* Unchanged name? */
if (access(name, R_OK) == 0)
return name;
/* Name with suffix tagged onto it? */
snprintf(path, sizeof(path), "%s.%s", name, SC_PKCS15_PROFILE_SUFFIX);
if (access(path, R_OK) == 0)
return path;
/* If it's got slashes, don't mess with it any further */
if (strchr(path, '/'))
return path;
/* Try directory */
snprintf(path, sizeof(path), "%s/%s",
SC_PKCS15_PROFILE_DIRECTORY, name);
if (access(path, R_OK) == 0)
return path;
snprintf(path, sizeof(path), "%s/%s.%s",
SC_PKCS15_PROFILE_DIRECTORY, name,
SC_PKCS15_PROFILE_SUFFIX);
if (access(path, R_OK) == 0)
return path;
return NULL;
}
void
sc_profile_set_pin_info(struct sc_profile *profile,
unsigned int id, const struct sc_pkcs15_pin_info *info)
{
struct pin_info *pi;
pi = new_pin(profile, id);
pi->pin = *info;
}
void
sc_profile_get_pin_info(struct sc_profile *profile,
unsigned int id, struct sc_pkcs15_pin_info *info)
{
struct pin_info *pi;
pi = new_pin(profile, id);
*info = pi->pin;
}
int
sc_profile_get_pin_id(struct sc_profile *profile,
unsigned int reference, unsigned int *id)
{
struct pin_info *pi;
for (pi = profile->pin_list; pi; pi = pi->next) {
if (pi->pin.reference == reference) {
*id = pi->id;
return 0;
}
}
return SC_ERROR_OBJECT_NOT_FOUND;
}
int
sc_profile_get_file(struct sc_profile *profile,
const char *name, struct sc_file **ret)
{
struct file_info *fi;
if ((fi = sc_profile_find_file(profile, name)) == NULL)
return SC_ERROR_FILE_NOT_FOUND;
sc_file_dup(ret, fi->file);
return 0;
}
int
sc_profile_get_path(struct sc_profile *profile,
const char *name, struct sc_path *ret)
{
struct file_info *fi;
if ((fi = sc_profile_find_file(profile, name)) == NULL)
return SC_ERROR_FILE_NOT_FOUND;
*ret = fi->file->path;
return 0;
}
int
sc_profile_get_file_by_path(struct sc_profile *profile,
const struct sc_path *path, struct sc_file **ret)
{
struct file_info *fi;
if ((fi = sc_profile_find_file_by_path(profile, path)) == NULL)
return SC_ERROR_FILE_NOT_FOUND;
sc_file_dup(ret, fi->file);
return 0;
}
/*
* Configuration file parser
*/
static void
init_state(struct state *cur, struct state *new_state)
{
memset(new_state, 0, sizeof(*new_state));
new_state->filename = cur->filename;
new_state->profile = cur->profile;
new_state->frame = cur;
}
static int
do_card_driver(struct state *cur, int argc, char **argv)
{
cur->profile->driver = strdup(argv[0]);
return 0;
}
static int
do_maxpinlength(struct state *cur, int argc, char **argv)
{
return get_uint(cur, argv[0], &cur->profile->pin_maxlen);
}
static int
do_minpinlength(struct state *cur, int argc, char **argv)
{
return get_uint(cur, argv[0], &cur->profile->pin_minlen);
}
static int
do_default_pin_type(struct state *cur, int argc, char **argv)
{
return map_str2int(cur, argv[0],
&cur->profile->pin_encoding, pinTypeNames);
}
static int
do_pin_pad_char(struct state *cur, int argc, char **argv)
{
return get_uint(cur, argv[0], &cur->profile->pin_pad_char);
}
static int
do_card_label(struct state *cur, int argc, char **argv)
{
struct sc_pkcs15_card *p15card = cur->profile->p15_card;
return setstr(&p15card->label, argv[0]);
}
static int
do_card_manufacturer(struct state *cur, int argc, char **argv)
{
struct sc_pkcs15_card *p15card = cur->profile->p15_card;
return setstr(&p15card->manufacturer_id, argv[0]);
}
/*
* Process a key block
*/
static int
process_key(struct state *cur, struct block *info,
const char *name, scconf_block *blk)
{
unsigned int type, id;
struct state state;
if (get_authid(cur, name, &type, &id))
return 1;
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;
}
ai = (struct auth_info *) calloc(1, sizeof(*ai));
ai->type = type;
ai->ref = ref;
*aip = ai;
return ai;
}
int
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);
return 1;
}
}
memcpy(ai->key, key, key_len);
ai->key_len = key_len;
return 0;
}
/*
* 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.
*/
static int
process_df(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 DF object.");
return 1;
}
if (!(state.file = new_file(cur, name, SC_FILE_TYPE_DF)))
return 1;
return process_block(&state, info, name, blk);
}
static int
process_ef(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 EF object.");
return 1;
}
if (!(state.file = new_file(cur, name, SC_FILE_TYPE_WORKING_EF)))
return 1;
return process_block(&state, info, name, blk);
}
static struct file_info *
new_file(struct state *cur, const char *name, unsigned int type)
{
struct sc_profile *profile = cur->profile;
struct file_info *info;
struct sc_file *file;
unsigned int df_type = 0;
if ((info = sc_profile_find_file(profile, name)) != NULL)
return info;
info = (struct file_info *) calloc(1, sizeof(*info));
info->ident = strdup(name);
/* Special cases for those EFs handled separately
* by the PKCS15 logic */
if (strncasecmp(name, "PKCS15-", 7)) {
file = init_file(type);
} else if (!strcasecmp(name+7, "TokenInfo")) {
file = profile->p15_card->file_tokeninfo;
} else if (!strcasecmp(name+7, "ODF")) {
file = profile->p15_card->file_odf;
} else if (!strcasecmp(name+7, "AppDF")) {
file = init_file(SC_FILE_TYPE_DF);
} else {
if (map_str2int(cur, name+7, &df_type, pkcs15DfNames))
return NULL;
file = init_file(SC_FILE_TYPE_WORKING_EF);
profile->df[df_type] = file;
}
assert(file);
if (file->type != type) {
parse_error(cur, "inconsistent file type (should be %s)",
(file->type == SC_FILE_TYPE_DF)? "DF" : "EF");
return NULL;
}
info->parent = cur->file;
info->file = file;
info->next = profile->ef_list;
profile->ef_list = info;
return info;
}
static int
do_file_type(struct state *cur, int argc, char **argv)
{
unsigned int type;
if (map_str2int(cur, argv[0], &type, fileTypeNames))
return 1;
cur->file->file->type = type;
return 0;
}
static int
do_file_path(struct state *cur, int argc, char **argv)
{
struct sc_file *file = cur->file->file;
struct sc_path *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], path);
if (!path->len || (path->len & 1)) {
parse_error(cur, "Invalid path length\n");
return 1;
}
file->id = (path->value[path->len-2] << 8)
| path->value[path->len-1];
return 0;
}
static int
do_fileid(struct state *cur, int argc, char **argv)
{
struct file_info *fi;
struct sc_file *df, *file = cur->file->file;
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) {
parse_error(cur, "Invalid file ID length\n");
return 1;
}
/* Get the DF, if any */
if ((fi = cur->file->parent) && (df = fi->file)) {
if (df->path.len == 0) {
parse_error(cur, "No path/fileid set for parent DF\n");
return 1;
}
if (df->path.len + 2 > sizeof(df->path)) {
parse_error(cur, "File path too long\n");
return 1;
}
*path = df->path;
}
memcpy(path->value + path->len, temp.value, 2);
path->len += 2;
file->id = (temp.value[0] << 8) | temp.value[1];
return 0;
}
static int
do_structure(struct state *cur, int argc, char **argv)
{
unsigned int ef_structure;
if (map_str2int(cur, argv[0], &ef_structure, fileStructureNames))
return 1;
cur->file->file->ef_structure = ef_structure;
return 0;
}
static int
do_size(struct state *cur, int argc, char **argv)
{
unsigned int size;
if (get_uint(cur, argv[0], &size))
return 1;
cur->file->file->size = size;
return 0;
}
static int
do_reclength(struct state *cur, int argc, char **argv)
{
unsigned int reclength;
if (get_uint(cur, argv[0], &reclength))
return 1;
cur->file->file->record_length = reclength;
return 0;
}
static int
do_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;
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;
} else {
file->namelen = sizeof(file->name);
res = sc_hex_to_bin(name, file->name, &file->namelen);
}
return res;
}
/*
* 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
do_acl(struct state *cur, int argc, char **argv)
{
struct sc_file *file = cur->file->file;
char *oper = 0, *what = 0;
while (argc--) {
unsigned int op, method, id;
oper = *argv++;
if ((what = strchr(oper, '=')) == NULL)
goto bad;
*what++ = '\0';
if (*what == '$') {
method = SC_AC_SYMBOLIC;
if (map_str2int(cur, what+1, &id, pinIdNames))
return 1;
} else
if (get_authid(cur, what, &method, &id))
goto bad;
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 {
const struct sc_acl_entry *acl;
if (map_str2int(cur, oper, &op, fileOpNames))
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);
sc_file_add_acl_entry(file, op, method, id);
}
}
return 0;
bad: parse_error(cur,
"Invalid ACL \"%s%s%s\"\n",
oper, what? "=" : "", what? what : "");
return 1;
}
static int
process_pin(struct state *cur, struct block *info,
const char *name, scconf_block *blk)
{
struct state state;
unsigned int id;
if (map_str2int(cur, name, &id, pinIdNames))
return 1;
init_state(cur, &state);
state.pin = new_pin(cur->profile, id);
return process_block(&state, info, name, blk);
}
static struct pin_info *
new_pin(struct sc_profile *profile, unsigned int id)
{
struct pin_info *pi, **tail;
for (tail = &profile->pin_list; (pi = *tail); tail = &pi->next) {
if (pi->id == id)
return pi;
}
pi = (struct pin_info *) calloc(1, sizeof(*pi));
pi->id = id;
pi->pin.type = profile->pin_encoding;
pi->pin.flags = 0x32;
pi->pin.min_length = profile->pin_minlen;
pi->pin.stored_length = profile->pin_maxlen;
pi->pin.pad_char = profile->pin_pad_char;
pi->pin.magic = SC_PKCS15_PIN_MAGIC;
pi->pin.reference = -1;
pi->pin.tries_left = 3;
*tail = pi;
return pi;
}
static int
do_pin_file(struct state *cur, int argc, char **argv)
{
struct file_info *fi;
const char *name = argv[0];
if (!(fi = sc_profile_find_file(cur->profile, name))) {
parse_error(cur, "unknown PIN file \"%s\"\n", name);
return 1;
}
cur->pin->file = fi;
return 0;
}
static int
do_pin_offset(struct state *cur, int argc, char **argv)
{
return get_uint(cur, argv[0], &cur->pin->file_offset);
}
static int
do_pin_attempts(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.tries_left = count;
return 0;
}
static int
do_pin_type(struct state *cur, int argc, char **argv)
{
unsigned int type;
if (map_str2int(cur, argv[0], &type, pinTypeNames))
return 1;
cur->pin->pin.type = type;
return 0;
}
static int
do_pin_reference(struct state *cur, int argc, char **argv)
{
unsigned int reference;
if (get_uint(cur, argv[0], &reference))
return 1;
cur->pin->pin.reference = reference;
return 0;
}
static int
do_pin_authid(struct state *cur, int argc, char **argv)
{
sc_pkcs15_format_id(argv[0], &cur->pin->pin.auth_id);
return 0;
}
static int
do_pin_minlength(struct state *cur, int argc, char **argv)
{
unsigned int len;
if (get_uint(cur, argv[0], &len))
return 1;
cur->pin->pin.min_length = len;
return 0;
}
static int
do_pin_maxlength(struct state *cur, int argc, char **argv)
{
unsigned int len;
if (get_uint(cur, argv[0], &len))
return 1;
cur->pin->pin.stored_length = len;
return 0;
}
/*
* Key section
*/
static struct command key_commands[] = {
{ "value", 1, 1, do_key_value },
{ NULL }
};
/*
* 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 },
{ "label", 1, 1, do_card_label },
{ "manufacturer", 1, 1, do_card_manufacturer},
{ NULL, 0, 0, NULL }
};
static struct block ci_blocks[] = {
{ "key", process_key, key_commands, NULL },
{ NULL }
};
/*
* 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 },
{ "size", 1, 1, do_size },
{ "record-length", 1, 1, do_reclength },
{ "AID", 1, 1, do_aid },
{ "ACL", 1, -1, do_acl },
{ NULL, 0, 0, NULL }
};
static struct block fs_blocks[] = {
{ "DF", process_df, fs_commands, fs_blocks },
{ "EF", process_ef, fs_commands, fs_blocks },
{ NULL, NULL, NULL, NULL }
};
/*
* Pin section
*/
static struct command pi_commands[] = {
{ "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},
{ NULL, 0, 0, NULL }
};
static struct block root_blocks[] = {
{ "filesystem", process_block, NULL, fs_blocks },
{ "cardinfo", process_block, ci_commands, ci_blocks },
{ "pin", process_pin, pi_commands, NULL },
{ NULL, NULL , NULL }
};
static struct block root_ops = {
"root", process_block, NULL, root_blocks
};
static int
process_command(struct state *cur, struct command *cmd_info, scconf_list *list)
{
const char *cmd = cmd_info->name;
char *argv[16];
unsigned int argc, max = 16;
/* count arguments first */
for (argc = 0; list; list = list->next) {
if (argc >= max)
goto toomany;
argv[argc++] = list->data;
}
if (argc < cmd_info->min_args) {
parse_error(cur, "%s: not enough arguments\n", cmd);
return 1;
}
if (0 <= cmd_info->max_args && cmd_info->max_args < argc) {
toomany: parse_error(cur, "%s: too many arguments\n", cmd);
return 1;
}
return cmd_info->func(cur, argc, argv);
}
static struct block *
find_block_handler(struct block *bp, const char *name)
{
if (bp == NULL)
return NULL;
for (; bp->name; bp++) {
if (!strcasecmp(bp->name, name))
return bp;
}
return NULL;
}
static struct command *
find_cmd_handler(struct command *cp, const char *name)
{
if (cp == NULL)
return NULL;
for (; cp->name; cp++) {
if (!strcasecmp(cp->name, name))
return cp;
}
return NULL;
}
static int
process_block(struct state *cur, struct block *info,
const char *name, scconf_block *blk)
{
scconf_item *item;
struct command *cp;
struct block *bp;
const char *cmd, *ident;
int res = 0;
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) {
parse_error(cur,
"Too many name components "
"in block name.");
return SC_ERROR_SYNTAX_ERROR;
}
ident = nlist->data;
}
#if 0
printf("Processing %s %s\n",
cmd, ident? ident : "");
#endif
if ((bp = find_block_handler(info->blk_info, cmd))) {
res = bp->handler(cur, bp, ident,
item->value.block);
continue;
}
} else
if (item->type == SCCONF_ITEM_TYPE_VALUE) {
if ((cp = find_cmd_handler(info->cmd_info, cmd))) {
res = process_command(cur, cp,
item->value.list);
continue;
}
}
parse_error(cur,
"Command \"%s\" not understood in this context.", cmd);
return SC_ERROR_SYNTAX_ERROR;
}
if (res > 0)
res = SC_ERROR_SYNTAX_ERROR;
return res;
}
static int
process_conf(struct sc_profile *profile, scconf_context *conf)
{
struct state state;
memset(&state, 0, sizeof(state));
state.filename = conf->filename;
state.profile = profile;
return process_block(&state, &root_ops, "root", conf->root);
}
static struct file_info *
sc_profile_find_file(struct sc_profile *pro, const char *name)
{
struct file_info *fi;
for (fi = pro->ef_list; fi; fi = fi->next) {
if (!strcasecmp(fi->ident, name))
return fi;
}
return NULL;
}
struct file_info *
sc_profile_find_file_by_path(struct sc_profile *pro, const struct sc_path *path)
{
struct file_info *fi;
struct sc_file *fp;
for (fi = pro->ef_list; fi; fi = fi->next) {
fp = fi->file;
if (fp->path.len == path->len
&& !memcmp(fp->path.value, path->value, path->len))
return fi;
}
return NULL;
}
void
sc_profile_set_secret(struct sc_profile *profile,
unsigned int type, unsigned int ref,
const u8 *key, size_t key_len)
{
struct auth_info *ai;
ai = new_key(profile, type, ref);
if (key_len)
memcpy(ai->key, key, key_len);
ai->key_len = key_len;
}
int
sc_profile_get_secret(struct sc_profile *profile,
unsigned int type, unsigned int ref,
u8 *key, size_t *len)
{
struct auth_info *ai, **aip;
for (aip = &profile->auth_list; (ai = *aip); aip = &ai->next) {
if (ai->type == type && ai->ref == ref) {
if (ai->key_len > *len)
return SC_ERROR_BUFFER_TOO_SMALL;
memcpy(key, ai->key, ai->key_len);
*len = ai->key_len;
return 0;
}
}
return SC_ERROR_OBJECT_NOT_FOUND;
}
void
sc_profile_set_so_pin(struct sc_profile *profile, const char *value)
{
if (!value)
return;
sc_profile_set_secret(profile, SC_AC_SYMBOLIC,
SC_PKCS15INIT_SO_PIN, (u8 *) value, strlen(value));
}
void
sc_profile_set_user_pin(struct sc_profile *profile, const char *value)
{
if (!value)
return;
sc_profile_set_secret(profile, SC_AC_SYMBOLIC,
SC_PKCS15INIT_USER_PIN, (u8 *) value, strlen(value));
}
/*
* Split up KEY0 or CHV1 into SC_AC_XXX and a number
*/
static int
get_authid(struct state *cur, const char *value,
unsigned int *type, unsigned int *num)
{
char temp[16];
int n;
if (isdigit((int) *value)) {
*num = 0;
return get_uint(cur, value, type);
}
n = strcspn(value, "0123456789");
strncpy(temp, value, n);
temp[n] = '\0';
if (map_str2int(cur, temp, type, aclNames))
return 1;
if (value[n])
return get_uint(cur, value + n, num);
*num = 0;
return 0;
}
static int
get_uint(struct state *cur, const char *value, unsigned int *vp)
{
const char *ep;
*vp = strtoul(value, (char **) &ep, 0);
if (*ep != '\0') {
parse_error(cur,
"invalid integer argument \"%s\"\n", value);
return 1;
}
return 0;
}
static int
map_str2int(struct state *cur, const char *value,
unsigned int *vp, struct map *map)
{
unsigned int n;
const char *what;
if (isdigit((int) *value))
return get_uint(cur, value, vp);
for (n = 0; map[n].name; n++) {
if (!strcasecmp(value, map[n].name)) {
*vp = map[n].val;
return 0;
}
}
/* 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);
return 1;
}
static int
setstr(char **strp, const char *value)
{
if (*strp)
free(*strp);
*strp = strdup(value);
return 0;
}
static void
parse_error(struct state *cur, const char *fmt, ...)
{
char buffer[1024], *sp;
va_list ap;
va_start(ap, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
if ((sp = strchr(buffer, '\n')) != NULL)
*sp = '\0';
if (cur->profile->cbs)
cur->profile->cbs->error("%s: %s",
cur->filename, buffer);
}