openssl/crypto/core_namemap.c
Richard Levitte f2182a4e6f Create internal number<->name mapping API
This can be used as a general name to identity map.

Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/8878)
2019-05-12 13:43:38 -07:00

211 lines
5.1 KiB
C

/*
* Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include "internal/namemap.h"
#include <openssl/lhash.h>
#include <openssl/safestack.h>
/* The namemap entry */
typedef struct {
int number;
const char *name;
char body[1]; /* Sized appropriately to contain the name */
} NAMEMAP_ENTRY;
DEFINE_LHASH_OF(NAMEMAP_ENTRY);
DEFINE_STACK_OF(NAMEMAP_ENTRY)
/* The namemap, which provides for bidirectional indexing */
struct ossl_namemap_st {
/* Flags */
unsigned int stored:1; /* If 1, it's stored in a library context */
CRYPTO_RWLOCK *lock;
LHASH_OF(NAMEMAP_ENTRY) *namenum; /* Name->number mapping */
STACK_OF(NAMEMAP_ENTRY) *numname; /* Number->name mapping */
};
/* LHASH callbacks */
static unsigned long namemap_hash(const NAMEMAP_ENTRY *n)
{
return OPENSSL_LH_strhash(n->name);
}
static int namemap_cmp(const NAMEMAP_ENTRY *a, const NAMEMAP_ENTRY *b)
{
return strcmp(a->name, b->name);
}
static void namemap_free(NAMEMAP_ENTRY *n)
{
OPENSSL_free(n);
}
/* OPENSSL_CTX_METHOD functions for a namemap stored in a library context */
static void *stored_namemap_new(OPENSSL_CTX *libctx)
{
OSSL_NAMEMAP *namemap = ossl_namemap_new();
if (namemap != NULL)
namemap->stored = 1;
return namemap;
}
static void stored_namemap_free(void *vnamemap)
{
OSSL_NAMEMAP *namemap = vnamemap;
/* Pretend it isn't stored, or ossl_namemap_free() will do nothing */
namemap->stored = 0;
ossl_namemap_free(namemap);
}
static const OPENSSL_CTX_METHOD stored_namemap_method = {
stored_namemap_new,
stored_namemap_free,
};
/* API functions */
OSSL_NAMEMAP *ossl_namemap_stored(OPENSSL_CTX *libctx)
{
return openssl_ctx_get_data(libctx, OPENSSL_CTX_NAMEMAP_INDEX,
&stored_namemap_method);
}
OSSL_NAMEMAP *ossl_namemap_new(void)
{
OSSL_NAMEMAP *namemap;
if ((namemap = OPENSSL_zalloc(sizeof(*namemap))) != NULL
&& (namemap->lock = CRYPTO_THREAD_lock_new()) != NULL
&& (namemap->numname = sk_NAMEMAP_ENTRY_new_null()) != NULL
&& (namemap->namenum =
lh_NAMEMAP_ENTRY_new(namemap_hash, namemap_cmp)) != NULL) {
return namemap;
}
ossl_namemap_free(namemap);
return NULL;
}
void ossl_namemap_free(OSSL_NAMEMAP *namemap)
{
if (namemap == NULL || namemap->stored)
return;
/* The elements will be freed by sk_NAMEMAP_ENTRY_pop_free() */
lh_NAMEMAP_ENTRY_free(namemap->namenum);
sk_NAMEMAP_ENTRY_pop_free(namemap->numname, namemap_free);
CRYPTO_THREAD_lock_free(namemap->lock);
OPENSSL_free(namemap);
}
/*
* TODO(3.0) It isn't currently possible to have a default namemap in the
* FIPS module because if init and cleanup constraints, so we currently
* disable the code that would allow it when FIPS_MODE is defined.
*/
const char *ossl_namemap_name(const OSSL_NAMEMAP *namemap, int number)
{
NAMEMAP_ENTRY *entry;
#ifndef FIPS_MODE
if (namemap == NULL)
namemap = ossl_namemap_stored(NULL);
#endif
if (namemap == NULL || number == 0)
return NULL;
CRYPTO_THREAD_read_lock(namemap->lock);
entry = sk_NAMEMAP_ENTRY_value(namemap->numname, number);
CRYPTO_THREAD_unlock(namemap->lock);
if (entry != NULL)
return entry->name;
return NULL;
}
int ossl_namemap_number(const OSSL_NAMEMAP *namemap, const char *name)
{
NAMEMAP_ENTRY *entry, template;
#ifndef FIPS_MODE
if (namemap == NULL)
namemap = ossl_namemap_stored(NULL);
#endif
if (namemap == NULL)
return 0;
template.name = name;
CRYPTO_THREAD_read_lock(namemap->lock);
entry = lh_NAMEMAP_ENTRY_retrieve(namemap->namenum, &template);
CRYPTO_THREAD_unlock(namemap->lock);
if (entry == NULL)
return 0;
return entry->number;
}
int ossl_namemap_add(OSSL_NAMEMAP *namemap, const char *name)
{
NAMEMAP_ENTRY *entry;
int number;
#ifndef FIPS_MODE
if (namemap == NULL)
namemap = ossl_namemap_stored(NULL);
#endif
if (name == NULL || namemap == NULL)
return 0;
if ((number = ossl_namemap_number(namemap, name)) != 0)
return number; /* Pretend success */
if ((entry = OPENSSL_zalloc(sizeof(*entry) + strlen(name))) == NULL)
goto err;
strcpy(entry->body, name);
entry->name = entry->body;
CRYPTO_THREAD_write_lock(namemap->lock);
entry->number = sk_NAMEMAP_ENTRY_push(namemap->numname, entry);
if (entry->number == 0)
goto err;
(void)lh_NAMEMAP_ENTRY_insert(namemap->namenum, entry);
if (lh_NAMEMAP_ENTRY_error(namemap->namenum))
goto err;
CRYPTO_THREAD_unlock(namemap->lock);
return entry->number;
err:
if (entry != NULL) {
if (entry->number != 0)
(void)sk_NAMEMAP_ENTRY_pop(namemap->numname);
lh_NAMEMAP_ENTRY_delete(namemap->namenum, entry);
CRYPTO_THREAD_unlock(namemap->lock);
}
return 0;
}