| /* |
| * 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; |
| } |
| } |
| } |