397 lines
8.4 KiB
C
397 lines
8.4 KiB
C
/*
|
|
* Cache authentication info
|
|
*
|
|
* Copyright (C) 2003, 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 <string.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#ifdef HAVE_STRINGS_H
|
|
#include <strings.h>
|
|
#endif
|
|
#include <assert.h>
|
|
#include <opensc/pkcs15.h>
|
|
#include <opensc/cardctl.h>
|
|
#include "profile.h"
|
|
#include "pkcs15-init.h"
|
|
|
|
#undef KEYCACHE_DEBUG
|
|
#define MAX_SECRET 32 /* sufficient for 128bit symmetric keys */
|
|
|
|
struct secret {
|
|
struct secret * next;
|
|
sc_path_t path;
|
|
int type, ref, named_pin;
|
|
size_t len;
|
|
unsigned char value[MAX_SECRET];
|
|
};
|
|
|
|
static struct secret * secret_cache = NULL;
|
|
static struct secret * named_pin[SC_PKCS15INIT_NPINS];
|
|
|
|
#ifdef KEYCACHE_DEBUG
|
|
static void sc_keycache_dump(void);
|
|
#endif
|
|
|
|
/*
|
|
* Check if a keycache entry matches the given type, reference
|
|
* and path.
|
|
*/
|
|
static int
|
|
__match_entry(struct secret *s, int type, int ref, const sc_path_t *path,
|
|
int match_prefix)
|
|
{
|
|
if ((type != -1 && s->type != type)
|
|
|| (ref != -1 && s->ref != ref))
|
|
return 0;
|
|
|
|
/* Compare the two paths */
|
|
if (match_prefix) {
|
|
/* Prefix match - the path argument given by
|
|
* the caller should be a prefix of the keycache
|
|
* entry.
|
|
*/
|
|
/* If the path is a wildcard, it's a match */
|
|
if (path == NULL)
|
|
return 1;
|
|
if (s->path.len > path->len)
|
|
return 0;
|
|
} else {
|
|
/* Exact match - path names must patch exactly.
|
|
* A NULL path argument is an empty path */
|
|
if (path == 0)
|
|
return (s->path.len == 0);
|
|
if (s->path.len != path->len)
|
|
return 0;
|
|
}
|
|
if (memcmp(s->path.value, path->value, s->path.len))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Find the secret, given a path name, type and reference.
|
|
* If none found, search for it in parent directories.
|
|
*/
|
|
static struct secret *
|
|
find_entry(const sc_path_t *path, int type, int ref, int match_prefix)
|
|
{
|
|
struct secret *s;
|
|
|
|
if (type == SC_AC_SYMBOLIC) {
|
|
if (0 <= ref && ref < SC_PKCS15INIT_NPINS
|
|
&& (s = named_pin[ref]) != NULL
|
|
&& __match_entry(s, SC_AC_CHV, -1, path, match_prefix))
|
|
return s;
|
|
return NULL;
|
|
}
|
|
|
|
for (s = secret_cache; s; s = s->next) {
|
|
if (__match_entry(s, type, ref, path, match_prefix))
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* Find a key with matching type/reference. If a path is
|
|
* given, find the entry with the longest matching prefix.
|
|
*/
|
|
static struct secret *
|
|
search_key(const sc_path_t *path, int type, int ref)
|
|
{
|
|
struct secret *best = NULL, *s;
|
|
|
|
if (type == SC_AC_SYMBOLIC) {
|
|
if (0 <= ref && ref < SC_PKCS15INIT_NPINS
|
|
&& (s = named_pin[ref]) != NULL
|
|
&& __match_entry(s, type, -1, path, 1))
|
|
return s;
|
|
return NULL;
|
|
}
|
|
|
|
for (s = secret_cache; s; s = s->next) {
|
|
if (s->len != 0
|
|
&& __match_entry(s, type, ref, path, 1)) {
|
|
/* Ignore if path shorter than the longest
|
|
* matched prefix.
|
|
*/
|
|
if (path == NULL || best == NULL
|
|
|| best->path.len < path->len)
|
|
best = s;
|
|
}
|
|
}
|
|
|
|
return best;
|
|
}
|
|
|
|
/*
|
|
* Store a secret in the cache
|
|
*/
|
|
static struct secret *
|
|
new_entry(const sc_path_t *path, int type, int ref)
|
|
{
|
|
struct secret *s;
|
|
|
|
s = (struct secret *) calloc(1, sizeof(*s));
|
|
if (s == NULL)
|
|
return NULL;
|
|
s->next = secret_cache;
|
|
secret_cache = s;
|
|
if (path)
|
|
s->path = *path;
|
|
if (type == SC_AC_SYMBOLIC) {
|
|
s->type = SC_AC_CHV;
|
|
s->ref = -1;
|
|
s->named_pin = ref;
|
|
} else {
|
|
s->type = type;
|
|
s->ref = ref;
|
|
s->named_pin = -1;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* Cache the given key
|
|
*/
|
|
int
|
|
sc_keycache_put_key(const sc_path_t *path, int type, int ref,
|
|
const unsigned char *secret, size_t len)
|
|
{
|
|
struct secret *s;
|
|
|
|
if (len > MAX_SECRET)
|
|
return SC_ERROR_BUFFER_TOO_SMALL;
|
|
|
|
if (!(s = find_entry(path, type, ref, 0))) {
|
|
s = new_entry(path, type, ref);
|
|
if (s == NULL)
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
if (type == SC_AC_SYMBOLIC)
|
|
named_pin[ref] = s;
|
|
}
|
|
|
|
memset(s->value, 0, sizeof(s->value));
|
|
memcpy(s->value, secret, len);
|
|
s->len = len;
|
|
|
|
#ifdef KEYCACHE_DEBUG
|
|
sc_keycache_dump();
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sc_keycache_put_pin(const sc_path_t *path, int ref, const u8 *pin)
|
|
{
|
|
return sc_keycache_put_key(path, SC_AC_CHV, ref, pin,
|
|
pin? strlen((const char *) pin) : 0);
|
|
}
|
|
|
|
/*
|
|
* Get a key/pin from the cache
|
|
*/
|
|
int
|
|
sc_keycache_get_key(const sc_path_t *path, int type, int ref,
|
|
unsigned char *key, size_t size)
|
|
{
|
|
struct secret *s;
|
|
|
|
if (!(s = search_key(path, type, ref)))
|
|
return SC_ERROR_OBJECT_NOT_FOUND;
|
|
|
|
if (s->len > size)
|
|
return SC_ERROR_BUFFER_TOO_SMALL;
|
|
memcpy(key, s->value, s->len);
|
|
return s->len;
|
|
}
|
|
|
|
const u8 *
|
|
sc_keycache_get_pin(const sc_path_t *path, int ref)
|
|
{
|
|
struct secret *s;
|
|
|
|
if (!(s = search_key(path, SC_AC_CHV, ref)))
|
|
return NULL;
|
|
|
|
return s->len? s->value : NULL;
|
|
}
|
|
|
|
/*
|
|
* Define a symbolic name for a PIN. This is used to define
|
|
* what $PIN and $SOPIN mean in a given context.
|
|
*/
|
|
int
|
|
sc_keycache_set_pin_name(const sc_path_t *path, int ref, int name)
|
|
{
|
|
struct secret *s, *old;
|
|
|
|
if (name < 0 || name >= SC_PKCS15INIT_NPINS)
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
/* If we had previously marked a PIN with this name,
|
|
* unlink it */
|
|
if ((old = named_pin[name]) != NULL) {
|
|
named_pin[name] = NULL;
|
|
old->named_pin = -1;
|
|
}
|
|
|
|
if (ref >= 0) {
|
|
/* Create the named PIN if it doesn't exist */
|
|
if (!(s = find_entry(path, SC_AC_CHV, ref, 0))) {
|
|
s = new_entry(path, SC_AC_CHV, ref);
|
|
if (s == NULL)
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
/* Set the pin name */
|
|
s->named_pin = name;
|
|
|
|
/* If the old SOPIN was just the name entry,
|
|
* copy over the name to the new entry */
|
|
if (old && old->ref == -1 && s->len == 0) {
|
|
memcpy(s->value, old->value, old->len);
|
|
s->len = old->len;
|
|
}
|
|
|
|
named_pin[name] = s;
|
|
}
|
|
|
|
#ifdef KEYCACHE_DEBUG
|
|
sc_keycache_dump();
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get the symbolic name of a PIN, if any
|
|
*/
|
|
int
|
|
sc_keycache_get_pin_name(const sc_path_t *path, int ref)
|
|
{
|
|
struct secret *s;
|
|
|
|
#ifdef KEYCACHE_DEBUG
|
|
printf("sc_keycache_get_pin_name(%s, %d)\n",
|
|
path? sc_print_path(path) : "any", ref);
|
|
#endif
|
|
|
|
if (!(s = find_entry(path, SC_AC_CHV, ref, 1)))
|
|
return -1;
|
|
return s->named_pin;
|
|
}
|
|
|
|
/*
|
|
* Get path and reference of symbolic PIN
|
|
*/
|
|
int
|
|
sc_keycache_find_named_pin(const sc_path_t *path, int name)
|
|
{
|
|
struct secret *s;
|
|
|
|
if (name < 0 || name >= SC_PKCS15INIT_NPINS
|
|
|| (s = named_pin[name]) == NULL
|
|
|| !__match_entry(s, SC_AC_CHV, -1, path, 1))
|
|
return -1;
|
|
|
|
return s->ref;
|
|
}
|
|
|
|
/*
|
|
* Zap one or more keys from the cache
|
|
*/
|
|
void
|
|
sc_keycache_forget_key(const sc_path_t *path, int type, int ref)
|
|
{
|
|
struct secret *s, **prev;
|
|
|
|
prev = &secret_cache;
|
|
while ((s = *prev) != NULL) {
|
|
if (__match_entry(s, type, ref, path, 1)) {
|
|
*prev = s->next;
|
|
if (s->named_pin >= 0 && s->named_pin < SC_PKCS15INIT_NPINS)
|
|
named_pin[s->named_pin] = NULL;
|
|
sc_mem_clear(s, sizeof(*s));
|
|
free(s);
|
|
} else {
|
|
prev = &s->next;
|
|
}
|
|
}
|
|
#ifdef KEYCACHE_DEBUG
|
|
sc_keycache_dump();
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Dump the keycache
|
|
*/
|
|
#ifdef KEYCACHE_DEBUG
|
|
void
|
|
sc_keycache_dump(void)
|
|
{
|
|
struct secret *s;
|
|
int j;
|
|
|
|
printf("== Keycache ==\n");
|
|
for (s = secret_cache; s; s = s->next) {
|
|
char buf[32];
|
|
|
|
switch (s->type) {
|
|
case SC_AC_CHV: printf("CHV"); break;
|
|
case SC_AC_AUT: printf("AUT"); break;
|
|
case SC_AC_PRO: printf("PRO"); break;
|
|
default: printf("%d/", s->type);
|
|
}
|
|
printf("%d %-16s\t", s->ref, sc_print_path(&s->path));
|
|
sc_bin_to_hex(s->value, s->len, buf, sizeof(buf), ':');
|
|
printf("key=%s", buf);
|
|
|
|
switch (s->named_pin) {
|
|
case SC_PKCS15INIT_SO_PIN:
|
|
printf(", SO PIN"); break;
|
|
case SC_PKCS15INIT_SO_PUK:
|
|
printf(", SO PUK"); break;
|
|
case SC_PKCS15INIT_USER_PIN:
|
|
printf(", USER PIN"); break;
|
|
case SC_PKCS15INIT_USER_PUK:
|
|
printf(", USER PUK"); break;
|
|
}
|
|
|
|
if (s->named_pin >= 0
|
|
&& named_pin[s->named_pin] != s)
|
|
printf(" [PTR MISMATCH!]");
|
|
printf("\n");
|
|
}
|
|
|
|
for (j = 0; j < SC_PKCS15INIT_NPINS; j++) {
|
|
if ((s = named_pin[j]) == NULL)
|
|
continue;
|
|
if (s->named_pin != j)
|
|
printf(" named_pin[%d] MISMATCH: name=%d\n",
|
|
j, s->named_pin);
|
|
}
|
|
}
|
|
#endif
|