blob: a461259bd23f973871df6627275080aa48a1cda0 [file] [log] [blame]
/*
* Simple memory cache for ini file parser
*
* This should be copied from somewhere but, unfortunately is reinvented.
*
* Copyright (C) 2019 James.Bottomley@HansenPartnership.com
*
* SPDX-License-Identifier: LGPL-2.1-only
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "openssl-pkcs11.h"
struct kv {
const char *key;
const char *value;
int len;
};
struct s_s {
const char *section;
struct kv *kvs;
int kv_count;
};
static struct s_s *s = NULL;
static int section_count = 0;
static struct s_s *section_get(const char *section)
{
int i;
for (i = 0; i < section_count; i++)
if (strcmp(s[i].section, section) == 0)
return &s[i];
return NULL;
}
static void remove_section(int sn)
{
section_count--;
/* last entry, just keep the reduced count (realloc will cope) */
if (sn == section_count) {
return;
}
/* just move everything over it and reduce the section count */
memcpy(&s[sn], &s[sn + 1], sizeof(struct s_s)*(section_count - sn));
}
static struct s_s *section_get_alloc(const char *section)
{
struct s_s *s_s = section_get(section);
if (s_s)
return s_s;
s = reallocarray(s, section_count + 1, sizeof(*s_s));
if (!s) {
fprintf(stderr, "realloc of section %s failed: %s",
section, strerror(errno));
return NULL;
}
s_s = &s[section_count++];
memset(s_s, 0, sizeof(*s_s));
s_s->section = section;
return s_s;
}
static struct kv *kv_get(struct s_s *s_s, const char *key)
{
int i;
for (i = 0; i < s_s->kv_count; i++)
if (strcmp(s_s->kvs[i].key, key) == 0)
return &s_s->kvs[i];
return NULL;
}
static struct kv *kv_get_alloc(struct s_s *s_s, const char *key)
{
struct kv *kv = kv_get(s_s, key);
if (kv)
return kv;
s_s->kvs = reallocarray(s_s->kvs, s_s->kv_count + 1, sizeof(*kv));
if (!s) {
fprintf(stderr, "realloc of section %s kvs failed: %s",
s_s->section, strerror(errno));
return NULL;
}
kv = &s_s->kvs[s_s->kv_count++];
memset(kv, 0, sizeof(*kv));
kv->key = key;
return kv;
}
static void cache_add_internal(struct s_s *s_s, const char *key,
const char *value, int len)
{
struct kv *kv = kv_get_alloc(s_s, key);
if (!kv)
return;
if (kv->value && kv->len == CACHE_PKEY)
/* discard const */
crypto_cache_free_pkey((void *)kv->value);
if (len == 0)
len = strlen(value);
kv->value = value;
kv->len = len;
}
void cache_add(const char *section, const char *key, const char *value, int len)
{
struct s_s *s_s = section_get_alloc(section);
if (!s_s)
return;
cache_add_internal(s_s, key, value, len);
}
void cache_add_by_secnum(int sec_num, const char *key, const char *value,
int len)
{
struct s_s *s_s;
if (sec_num > section_count)
return;
s_s = &s[sec_num];
cache_add_internal(s_s, key, value, len);
}
static const char *
cache_get_internal(struct s_s *sec, const char *key, int *len)
{
struct kv *kv;
kv = kv_get(sec, key);
if (!kv)
return NULL;
if (len)
*len = kv->len;
return kv->value;
}
const char *cache_get(const char *section, const char *key, int *len)
{
struct s_s *sec = section_get(section);
if (!sec)
return NULL;
return cache_get_internal(sec, key, len);
}
const char *cache_get_by_secnum(int sec_num, const char *key, int *len)
{
struct s_s *sec;
if (sec_num >= section_count)
return NULL;
sec = &s[sec_num];
return cache_get_internal(sec, key, len);
}
const char *cache_get_section(int sc)
{
if (sc >= section_count)
return NULL;
return s[sc].section;
}
int cache_get_sections(void)
{
return section_count;
}
void cache_load_crypto_keys(void)
{
int i;
/* every section apart from global must have public key and a
* private key */
for (i = 1; i < section_count; i++) {
struct kv *kv;
kv = kv_get(&s[i], INI_PUBLIC_KEY);
if (!kv) {
kv = kv_get(&s[i], INI_CERT);
if (kv)
crypto_load_cert(i, kv->value);
} else {
crypto_load_public_key(i, kv->value);
}
if (!kv) {
fprintf(stderr, "key '%s' has no '%s' or '%s' value\n",
s[i].section, INI_PUBLIC_KEY, INI_CERT);
/*
* a bit nasty, but removing the section
* in-place means we have to do over the value
* of i we just used
*/
remove_section(i--);
continue;
}
}
}