763 lines
22 KiB
C
763 lines
22 KiB
C
|
/*
|
||
|
* Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
|
||
|
* Copyright (c) 2019, Oracle and/or its affiliates. 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 <string.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <openssl/err.h>
|
||
|
#include "internal/propertyerr.h"
|
||
|
#include "internal/property.h"
|
||
|
#include "crypto/ctype.h"
|
||
|
#include "internal/nelem.h"
|
||
|
#include "property_local.h"
|
||
|
#include "internal/e_os.h"
|
||
|
|
||
|
DEFINE_STACK_OF(OSSL_PROPERTY_DEFINITION)
|
||
|
|
||
|
static const char *skip_space(const char *s)
|
||
|
{
|
||
|
while (ossl_isspace(*s))
|
||
|
s++;
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
static int match_ch(const char *t[], char m)
|
||
|
{
|
||
|
const char *s = *t;
|
||
|
|
||
|
if (*s == m) {
|
||
|
*t = skip_space(s + 1);
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#define MATCH(s, m) match(s, m, sizeof(m) - 1)
|
||
|
|
||
|
static int match(const char *t[], const char m[], size_t m_len)
|
||
|
{
|
||
|
const char *s = *t;
|
||
|
|
||
|
if (OPENSSL_strncasecmp(s, m, m_len) == 0) {
|
||
|
*t = skip_space(s + m_len);
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int parse_name(OSSL_LIB_CTX *ctx, const char *t[], int create,
|
||
|
OSSL_PROPERTY_IDX *idx)
|
||
|
{
|
||
|
char name[100];
|
||
|
int err = 0;
|
||
|
size_t i = 0;
|
||
|
const char *s = *t;
|
||
|
int user_name = 0;
|
||
|
|
||
|
for (;;) {
|
||
|
if (!ossl_isalpha(*s)) {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_IDENTIFIER,
|
||
|
"HERE-->%s", *t);
|
||
|
return 0;
|
||
|
}
|
||
|
do {
|
||
|
if (i < sizeof(name) - 1)
|
||
|
name[i++] = ossl_tolower(*s);
|
||
|
else
|
||
|
err = 1;
|
||
|
} while (*++s == '_' || ossl_isalnum(*s));
|
||
|
if (*s != '.')
|
||
|
break;
|
||
|
user_name = 1;
|
||
|
if (i < sizeof(name) - 1)
|
||
|
name[i++] = *s;
|
||
|
else
|
||
|
err = 1;
|
||
|
s++;
|
||
|
}
|
||
|
name[i] = '\0';
|
||
|
if (err) {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_NAME_TOO_LONG, "HERE-->%s", *t);
|
||
|
return 0;
|
||
|
}
|
||
|
*t = skip_space(s);
|
||
|
*idx = ossl_property_name(ctx, name, user_name && create);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int parse_number(const char *t[], OSSL_PROPERTY_DEFINITION *res)
|
||
|
{
|
||
|
const char *s = *t;
|
||
|
int64_t v = 0;
|
||
|
|
||
|
do {
|
||
|
if (!ossl_isdigit(*s)) {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_A_DECIMAL_DIGIT,
|
||
|
"HERE-->%s", *t);
|
||
|
return 0;
|
||
|
}
|
||
|
/* overflow check */
|
||
|
if (v > ((INT64_MAX - (*s - '0')) / 10)) {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
|
||
|
"Property %s overflows", *t);
|
||
|
return 0;
|
||
|
}
|
||
|
v = v * 10 + (*s++ - '0');
|
||
|
} while (ossl_isdigit(*s));
|
||
|
if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_A_DECIMAL_DIGIT,
|
||
|
"HERE-->%s", *t);
|
||
|
return 0;
|
||
|
}
|
||
|
*t = skip_space(s);
|
||
|
res->type = OSSL_PROPERTY_TYPE_NUMBER;
|
||
|
res->v.int_val = v;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int parse_hex(const char *t[], OSSL_PROPERTY_DEFINITION *res)
|
||
|
{
|
||
|
const char *s = *t;
|
||
|
int64_t v = 0;
|
||
|
int sval;
|
||
|
|
||
|
do {
|
||
|
if (ossl_isdigit(*s)) {
|
||
|
sval = *s - '0';
|
||
|
} else if (ossl_isxdigit(*s)) {
|
||
|
sval = ossl_tolower(*s) - 'a' + 10;
|
||
|
} else {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_HEXADECIMAL_DIGIT,
|
||
|
"%s", *t);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (v > ((INT64_MAX - sval) / 16)) {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
|
||
|
"Property %s overflows", *t);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
v <<= 4;
|
||
|
v += sval;
|
||
|
} while (ossl_isxdigit(*++s));
|
||
|
if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_HEXADECIMAL_DIGIT,
|
||
|
"HERE-->%s", *t);
|
||
|
return 0;
|
||
|
}
|
||
|
*t = skip_space(s);
|
||
|
res->type = OSSL_PROPERTY_TYPE_NUMBER;
|
||
|
res->v.int_val = v;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int parse_oct(const char *t[], OSSL_PROPERTY_DEFINITION *res)
|
||
|
{
|
||
|
const char *s = *t;
|
||
|
int64_t v = 0;
|
||
|
|
||
|
do {
|
||
|
if (*s == '9' || *s == '8' || !ossl_isdigit(*s)) {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_OCTAL_DIGIT,
|
||
|
"HERE-->%s", *t);
|
||
|
return 0;
|
||
|
}
|
||
|
if (v > ((INT64_MAX - (*s - '0')) / 8)) {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
|
||
|
"Property %s overflows", *t);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
v = (v << 3) + (*s - '0');
|
||
|
} while (ossl_isdigit(*++s) && *s != '9' && *s != '8');
|
||
|
if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_OCTAL_DIGIT,
|
||
|
"HERE-->%s", *t);
|
||
|
return 0;
|
||
|
}
|
||
|
*t = skip_space(s);
|
||
|
res->type = OSSL_PROPERTY_TYPE_NUMBER;
|
||
|
res->v.int_val = v;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int parse_string(OSSL_LIB_CTX *ctx, const char *t[], char delim,
|
||
|
OSSL_PROPERTY_DEFINITION *res, const int create)
|
||
|
{
|
||
|
char v[1000];
|
||
|
const char *s = *t;
|
||
|
size_t i = 0;
|
||
|
int err = 0;
|
||
|
|
||
|
while (*s != '\0' && *s != delim) {
|
||
|
if (i < sizeof(v) - 1)
|
||
|
v[i++] = *s;
|
||
|
else
|
||
|
err = 1;
|
||
|
s++;
|
||
|
}
|
||
|
if (*s == '\0') {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_NO_MATCHING_STRING_DELIMITER,
|
||
|
"HERE-->%c%s", delim, *t);
|
||
|
return 0;
|
||
|
}
|
||
|
v[i] = '\0';
|
||
|
if (err) {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_STRING_TOO_LONG, "HERE-->%s", *t);
|
||
|
} else {
|
||
|
res->v.str_val = ossl_property_value(ctx, v, create);
|
||
|
}
|
||
|
*t = skip_space(s + 1);
|
||
|
res->type = OSSL_PROPERTY_TYPE_STRING;
|
||
|
return !err;
|
||
|
}
|
||
|
|
||
|
static int parse_unquoted(OSSL_LIB_CTX *ctx, const char *t[],
|
||
|
OSSL_PROPERTY_DEFINITION *res, const int create)
|
||
|
{
|
||
|
char v[1000];
|
||
|
const char *s = *t;
|
||
|
size_t i = 0;
|
||
|
int err = 0;
|
||
|
|
||
|
if (*s == '\0' || *s == ',')
|
||
|
return 0;
|
||
|
while (ossl_isprint(*s) && !ossl_isspace(*s) && *s != ',') {
|
||
|
if (i < sizeof(v) - 1)
|
||
|
v[i++] = ossl_tolower(*s);
|
||
|
else
|
||
|
err = 1;
|
||
|
s++;
|
||
|
}
|
||
|
if (!ossl_isspace(*s) && *s != '\0' && *s != ',') {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_NOT_AN_ASCII_CHARACTER,
|
||
|
"HERE-->%s", s);
|
||
|
return 0;
|
||
|
}
|
||
|
v[i] = 0;
|
||
|
if (err)
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_STRING_TOO_LONG, "HERE-->%s", *t);
|
||
|
else if ((res->v.str_val = ossl_property_value(ctx, v, create)) == 0)
|
||
|
err = 1;
|
||
|
*t = skip_space(s);
|
||
|
res->type = OSSL_PROPERTY_TYPE_STRING;
|
||
|
return !err;
|
||
|
}
|
||
|
|
||
|
static int parse_value(OSSL_LIB_CTX *ctx, const char *t[],
|
||
|
OSSL_PROPERTY_DEFINITION *res, int create)
|
||
|
{
|
||
|
const char *s = *t;
|
||
|
int r = 0;
|
||
|
|
||
|
if (*s == '"' || *s == '\'') {
|
||
|
s++;
|
||
|
r = parse_string(ctx, &s, s[-1], res, create);
|
||
|
} else if (*s == '+') {
|
||
|
s++;
|
||
|
r = parse_number(&s, res);
|
||
|
} else if (*s == '-') {
|
||
|
s++;
|
||
|
r = parse_number(&s, res);
|
||
|
res->v.int_val = -res->v.int_val;
|
||
|
} else if (*s == '0' && s[1] == 'x') {
|
||
|
s += 2;
|
||
|
r = parse_hex(&s, res);
|
||
|
} else if (*s == '0' && ossl_isdigit(s[1])) {
|
||
|
s++;
|
||
|
r = parse_oct(&s, res);
|
||
|
} else if (ossl_isdigit(*s)) {
|
||
|
return parse_number(t, res);
|
||
|
} else if (ossl_isalpha(*s))
|
||
|
return parse_unquoted(ctx, t, res, create);
|
||
|
if (r)
|
||
|
*t = s;
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
static int pd_compare(const OSSL_PROPERTY_DEFINITION *const *p1,
|
||
|
const OSSL_PROPERTY_DEFINITION *const *p2)
|
||
|
{
|
||
|
const OSSL_PROPERTY_DEFINITION *pd1 = *p1;
|
||
|
const OSSL_PROPERTY_DEFINITION *pd2 = *p2;
|
||
|
|
||
|
if (pd1->name_idx < pd2->name_idx)
|
||
|
return -1;
|
||
|
if (pd1->name_idx > pd2->name_idx)
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void pd_free(OSSL_PROPERTY_DEFINITION *pd)
|
||
|
{
|
||
|
OPENSSL_free(pd);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Convert a stack of property definitions and queries into a fixed array.
|
||
|
* The items are sorted for efficient query. The stack is not freed.
|
||
|
* This function also checks for duplicated names and returns an error if
|
||
|
* any exist.
|
||
|
*/
|
||
|
static OSSL_PROPERTY_LIST *
|
||
|
stack_to_property_list(OSSL_LIB_CTX *ctx,
|
||
|
STACK_OF(OSSL_PROPERTY_DEFINITION) *sk)
|
||
|
{
|
||
|
const int n = sk_OSSL_PROPERTY_DEFINITION_num(sk);
|
||
|
OSSL_PROPERTY_LIST *r;
|
||
|
OSSL_PROPERTY_IDX prev_name_idx = 0;
|
||
|
int i;
|
||
|
|
||
|
r = OPENSSL_malloc(sizeof(*r)
|
||
|
+ (n <= 0 ? 0 : n - 1) * sizeof(r->properties[0]));
|
||
|
if (r != NULL) {
|
||
|
sk_OSSL_PROPERTY_DEFINITION_sort(sk);
|
||
|
|
||
|
r->has_optional = 0;
|
||
|
for (i = 0; i < n; i++) {
|
||
|
r->properties[i] = *sk_OSSL_PROPERTY_DEFINITION_value(sk, i);
|
||
|
r->has_optional |= r->properties[i].optional;
|
||
|
|
||
|
/* Check for duplicated names */
|
||
|
if (i > 0 && r->properties[i].name_idx == prev_name_idx) {
|
||
|
OPENSSL_free(r);
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
|
||
|
"Duplicated name `%s'",
|
||
|
ossl_property_name_str(ctx, prev_name_idx));
|
||
|
return NULL;
|
||
|
}
|
||
|
prev_name_idx = r->properties[i].name_idx;
|
||
|
}
|
||
|
r->num_properties = n;
|
||
|
}
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
OSSL_PROPERTY_LIST *ossl_parse_property(OSSL_LIB_CTX *ctx, const char *defn)
|
||
|
{
|
||
|
OSSL_PROPERTY_DEFINITION *prop = NULL;
|
||
|
OSSL_PROPERTY_LIST *res = NULL;
|
||
|
STACK_OF(OSSL_PROPERTY_DEFINITION) *sk;
|
||
|
const char *s = defn;
|
||
|
int done;
|
||
|
|
||
|
if (s == NULL || (sk = sk_OSSL_PROPERTY_DEFINITION_new(&pd_compare)) == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
s = skip_space(s);
|
||
|
done = *s == '\0';
|
||
|
while (!done) {
|
||
|
const char *start = s;
|
||
|
|
||
|
prop = OPENSSL_malloc(sizeof(*prop));
|
||
|
if (prop == NULL)
|
||
|
goto err;
|
||
|
memset(&prop->v, 0, sizeof(prop->v));
|
||
|
prop->optional = 0;
|
||
|
if (!parse_name(ctx, &s, 1, &prop->name_idx))
|
||
|
goto err;
|
||
|
prop->oper = OSSL_PROPERTY_OPER_EQ;
|
||
|
if (prop->name_idx == 0) {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_PARSE_FAILED,
|
||
|
"Unknown name HERE-->%s", start);
|
||
|
goto err;
|
||
|
}
|
||
|
if (match_ch(&s, '=')) {
|
||
|
if (!parse_value(ctx, &s, prop, 1)) {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_NO_VALUE,
|
||
|
"HERE-->%s", start);
|
||
|
goto err;
|
||
|
}
|
||
|
} else {
|
||
|
/* A name alone means a true Boolean */
|
||
|
prop->type = OSSL_PROPERTY_TYPE_STRING;
|
||
|
prop->v.str_val = OSSL_PROPERTY_TRUE;
|
||
|
}
|
||
|
|
||
|
if (!sk_OSSL_PROPERTY_DEFINITION_push(sk, prop))
|
||
|
goto err;
|
||
|
prop = NULL;
|
||
|
done = !match_ch(&s, ',');
|
||
|
}
|
||
|
if (*s != '\0') {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_TRAILING_CHARACTERS,
|
||
|
"HERE-->%s", s);
|
||
|
goto err;
|
||
|
}
|
||
|
res = stack_to_property_list(ctx, sk);
|
||
|
|
||
|
err:
|
||
|
OPENSSL_free(prop);
|
||
|
sk_OSSL_PROPERTY_DEFINITION_pop_free(sk, &pd_free);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
OSSL_PROPERTY_LIST *ossl_parse_query(OSSL_LIB_CTX *ctx, const char *s,
|
||
|
int create_values)
|
||
|
{
|
||
|
STACK_OF(OSSL_PROPERTY_DEFINITION) *sk;
|
||
|
OSSL_PROPERTY_LIST *res = NULL;
|
||
|
OSSL_PROPERTY_DEFINITION *prop = NULL;
|
||
|
int done;
|
||
|
|
||
|
if (s == NULL || (sk = sk_OSSL_PROPERTY_DEFINITION_new(&pd_compare)) == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
s = skip_space(s);
|
||
|
done = *s == '\0';
|
||
|
while (!done) {
|
||
|
prop = OPENSSL_malloc(sizeof(*prop));
|
||
|
if (prop == NULL)
|
||
|
goto err;
|
||
|
memset(&prop->v, 0, sizeof(prop->v));
|
||
|
|
||
|
if (match_ch(&s, '-')) {
|
||
|
prop->oper = OSSL_PROPERTY_OVERRIDE;
|
||
|
prop->optional = 0;
|
||
|
if (!parse_name(ctx, &s, 1, &prop->name_idx))
|
||
|
goto err;
|
||
|
goto skip_value;
|
||
|
}
|
||
|
prop->optional = match_ch(&s, '?');
|
||
|
if (!parse_name(ctx, &s, 1, &prop->name_idx))
|
||
|
goto err;
|
||
|
|
||
|
if (match_ch(&s, '=')) {
|
||
|
prop->oper = OSSL_PROPERTY_OPER_EQ;
|
||
|
} else if (MATCH(&s, "!=")) {
|
||
|
prop->oper = OSSL_PROPERTY_OPER_NE;
|
||
|
} else {
|
||
|
/* A name alone is a Boolean comparison for true */
|
||
|
prop->oper = OSSL_PROPERTY_OPER_EQ;
|
||
|
prop->type = OSSL_PROPERTY_TYPE_STRING;
|
||
|
prop->v.str_val = OSSL_PROPERTY_TRUE;
|
||
|
goto skip_value;
|
||
|
}
|
||
|
if (!parse_value(ctx, &s, prop, create_values))
|
||
|
prop->type = OSSL_PROPERTY_TYPE_VALUE_UNDEFINED;
|
||
|
|
||
|
skip_value:
|
||
|
if (!sk_OSSL_PROPERTY_DEFINITION_push(sk, prop))
|
||
|
goto err;
|
||
|
prop = NULL;
|
||
|
done = !match_ch(&s, ',');
|
||
|
}
|
||
|
if (*s != '\0') {
|
||
|
ERR_raise_data(ERR_LIB_PROP, PROP_R_TRAILING_CHARACTERS,
|
||
|
"HERE-->%s", s);
|
||
|
goto err;
|
||
|
}
|
||
|
res = stack_to_property_list(ctx, sk);
|
||
|
|
||
|
err:
|
||
|
OPENSSL_free(prop);
|
||
|
sk_OSSL_PROPERTY_DEFINITION_pop_free(sk, &pd_free);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Compare a query against a definition.
|
||
|
* Return the number of clauses matched or -1 if a mandatory clause is false.
|
||
|
*/
|
||
|
int ossl_property_match_count(const OSSL_PROPERTY_LIST *query,
|
||
|
const OSSL_PROPERTY_LIST *defn)
|
||
|
{
|
||
|
const OSSL_PROPERTY_DEFINITION *const q = query->properties;
|
||
|
const OSSL_PROPERTY_DEFINITION *const d = defn->properties;
|
||
|
int i = 0, j = 0, matches = 0;
|
||
|
OSSL_PROPERTY_OPER oper;
|
||
|
|
||
|
while (i < query->num_properties) {
|
||
|
if ((oper = q[i].oper) == OSSL_PROPERTY_OVERRIDE) {
|
||
|
i++;
|
||
|
continue;
|
||
|
}
|
||
|
if (j < defn->num_properties) {
|
||
|
if (q[i].name_idx > d[j].name_idx) { /* skip defn, not in query */
|
||
|
j++;
|
||
|
continue;
|
||
|
}
|
||
|
if (q[i].name_idx == d[j].name_idx) { /* both in defn and query */
|
||
|
const int eq = q[i].type == d[j].type
|
||
|
&& memcmp(&q[i].v, &d[j].v, sizeof(q[i].v)) == 0;
|
||
|
|
||
|
if ((eq && oper == OSSL_PROPERTY_OPER_EQ)
|
||
|
|| (!eq && oper == OSSL_PROPERTY_OPER_NE))
|
||
|
matches++;
|
||
|
else if (!q[i].optional)
|
||
|
return -1;
|
||
|
i++;
|
||
|
j++;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Handle the cases of a missing value and a query with no corresponding
|
||
|
* definition. The former fails for any comparison except inequality,
|
||
|
* the latter is treated as a comparison against the Boolean false.
|
||
|
*/
|
||
|
if (q[i].type == OSSL_PROPERTY_TYPE_VALUE_UNDEFINED) {
|
||
|
if (oper == OSSL_PROPERTY_OPER_NE)
|
||
|
matches++;
|
||
|
else if (!q[i].optional)
|
||
|
return -1;
|
||
|
} else if (q[i].type != OSSL_PROPERTY_TYPE_STRING
|
||
|
|| (oper == OSSL_PROPERTY_OPER_EQ
|
||
|
&& q[i].v.str_val != OSSL_PROPERTY_FALSE)
|
||
|
|| (oper == OSSL_PROPERTY_OPER_NE
|
||
|
&& q[i].v.str_val == OSSL_PROPERTY_FALSE)) {
|
||
|
if (!q[i].optional)
|
||
|
return -1;
|
||
|
} else {
|
||
|
matches++;
|
||
|
}
|
||
|
i++;
|
||
|
}
|
||
|
return matches;
|
||
|
}
|
||
|
|
||
|
void ossl_property_free(OSSL_PROPERTY_LIST *p)
|
||
|
{
|
||
|
OPENSSL_free(p);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Merge two property lists.
|
||
|
* If there is a common name, the one from the first list is used.
|
||
|
*/
|
||
|
OSSL_PROPERTY_LIST *ossl_property_merge(const OSSL_PROPERTY_LIST *a,
|
||
|
const OSSL_PROPERTY_LIST *b)
|
||
|
{
|
||
|
const OSSL_PROPERTY_DEFINITION *const ap = a->properties;
|
||
|
const OSSL_PROPERTY_DEFINITION *const bp = b->properties;
|
||
|
const OSSL_PROPERTY_DEFINITION *copy;
|
||
|
OSSL_PROPERTY_LIST *r;
|
||
|
int i, j, n;
|
||
|
const int t = a->num_properties + b->num_properties;
|
||
|
|
||
|
r = OPENSSL_malloc(sizeof(*r)
|
||
|
+ (t == 0 ? 0 : t - 1) * sizeof(r->properties[0]));
|
||
|
if (r == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
r->has_optional = 0;
|
||
|
for (i = j = n = 0; i < a->num_properties || j < b->num_properties; n++) {
|
||
|
if (i >= a->num_properties) {
|
||
|
copy = &bp[j++];
|
||
|
} else if (j >= b->num_properties) {
|
||
|
copy = &ap[i++];
|
||
|
} else if (ap[i].name_idx <= bp[j].name_idx) {
|
||
|
if (ap[i].name_idx == bp[j].name_idx)
|
||
|
j++;
|
||
|
copy = &ap[i++];
|
||
|
} else {
|
||
|
copy = &bp[j++];
|
||
|
}
|
||
|
memcpy(r->properties + n, copy, sizeof(r->properties[0]));
|
||
|
r->has_optional |= copy->optional;
|
||
|
}
|
||
|
r->num_properties = n;
|
||
|
if (n != t)
|
||
|
r = OPENSSL_realloc(r, sizeof(*r) + (n - 1) * sizeof(r->properties[0]));
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
int ossl_property_parse_init(OSSL_LIB_CTX *ctx)
|
||
|
{
|
||
|
static const char *const predefined_names[] = {
|
||
|
"provider", /* Name of provider (default, legacy, fips) */
|
||
|
"version", /* Version number of this provider */
|
||
|
"fips", /* FIPS validated or FIPS supporting algorithm */
|
||
|
"output", /* Output type for encoders */
|
||
|
"input", /* Input type for decoders */
|
||
|
"structure", /* Structure name for encoders and decoders */
|
||
|
};
|
||
|
size_t i;
|
||
|
|
||
|
for (i = 0; i < OSSL_NELEM(predefined_names); i++)
|
||
|
if (ossl_property_name(ctx, predefined_names[i], 1) == 0)
|
||
|
goto err;
|
||
|
|
||
|
/*
|
||
|
* Pre-populate the two Boolean values. We must do them before any other
|
||
|
* values and in this order so that we get the same index as the global
|
||
|
* OSSL_PROPERTY_TRUE and OSSL_PROPERTY_FALSE values
|
||
|
*/
|
||
|
if ((ossl_property_value(ctx, "yes", 1) != OSSL_PROPERTY_TRUE)
|
||
|
|| (ossl_property_value(ctx, "no", 1) != OSSL_PROPERTY_FALSE))
|
||
|
goto err;
|
||
|
|
||
|
return 1;
|
||
|
err:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void put_char(char ch, char **buf, size_t *remain, size_t *needed)
|
||
|
{
|
||
|
if (*remain == 0) {
|
||
|
++*needed;
|
||
|
return;
|
||
|
}
|
||
|
if (*remain == 1)
|
||
|
**buf = '\0';
|
||
|
else
|
||
|
**buf = ch;
|
||
|
++*buf;
|
||
|
++*needed;
|
||
|
--*remain;
|
||
|
}
|
||
|
|
||
|
static void put_str(const char *str, char **buf, size_t *remain, size_t *needed)
|
||
|
{
|
||
|
size_t olen, len, i;
|
||
|
char quote = '\0';
|
||
|
int quotes;
|
||
|
|
||
|
len = olen = strlen(str);
|
||
|
*needed += len;
|
||
|
|
||
|
/*
|
||
|
* Check to see if we need quotes or not.
|
||
|
* Characters that are legal in a PropertyName don't need quoting.
|
||
|
* We simply assume all others require quotes.
|
||
|
*/
|
||
|
for (i = 0; i < len; i++)
|
||
|
if (!ossl_isalnum(str[i]) && str[i] != '.' && str[i] != '_') {
|
||
|
/* Default to single quotes ... */
|
||
|
if (quote == '\0')
|
||
|
quote = '\'';
|
||
|
/* ... but use double quotes if a single is present */
|
||
|
if (str[i] == '\'')
|
||
|
quote = '"';
|
||
|
}
|
||
|
|
||
|
quotes = quote != '\0';
|
||
|
if (*remain == 0) {
|
||
|
*needed += 2 * quotes;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (quotes)
|
||
|
put_char(quote, buf, remain, needed);
|
||
|
|
||
|
if (*remain < len + 1 + quotes)
|
||
|
len = *remain - 1;
|
||
|
|
||
|
if (len > 0) {
|
||
|
memcpy(*buf, str, len);
|
||
|
*buf += len;
|
||
|
*remain -= len;
|
||
|
}
|
||
|
|
||
|
if (quotes)
|
||
|
put_char(quote, buf, remain, needed);
|
||
|
|
||
|
if (len < olen && *remain == 1) {
|
||
|
**buf = '\0';
|
||
|
++*buf;
|
||
|
--*remain;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void put_num(int64_t val, char **buf, size_t *remain, size_t *needed)
|
||
|
{
|
||
|
int64_t tmpval = val;
|
||
|
size_t len = 1;
|
||
|
|
||
|
if (tmpval < 0) {
|
||
|
len++;
|
||
|
tmpval = -tmpval;
|
||
|
}
|
||
|
for (; tmpval > 9; len++, tmpval /= 10);
|
||
|
|
||
|
*needed += len;
|
||
|
|
||
|
if (*remain == 0)
|
||
|
return;
|
||
|
|
||
|
BIO_snprintf(*buf, *remain, "%lld", (long long int)val);
|
||
|
if (*remain < len) {
|
||
|
*buf += *remain;
|
||
|
*remain = 0;
|
||
|
} else {
|
||
|
*buf += len;
|
||
|
*remain -= len;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
size_t ossl_property_list_to_string(OSSL_LIB_CTX *ctx,
|
||
|
const OSSL_PROPERTY_LIST *list, char *buf,
|
||
|
size_t bufsize)
|
||
|
{
|
||
|
int i;
|
||
|
const OSSL_PROPERTY_DEFINITION *prop = NULL;
|
||
|
size_t needed = 0;
|
||
|
const char *val;
|
||
|
|
||
|
if (list == NULL) {
|
||
|
if (bufsize > 0)
|
||
|
*buf = '\0';
|
||
|
return 1;
|
||
|
}
|
||
|
if (list->num_properties != 0)
|
||
|
prop = &list->properties[list->num_properties - 1];
|
||
|
for (i = 0; i < list->num_properties; i++, prop--) {
|
||
|
/* Skip invalid names */
|
||
|
if (prop->name_idx == 0)
|
||
|
continue;
|
||
|
|
||
|
if (needed > 0)
|
||
|
put_char(',', &buf, &bufsize, &needed);
|
||
|
|
||
|
if (prop->optional)
|
||
|
put_char('?', &buf, &bufsize, &needed);
|
||
|
else if (prop->oper == OSSL_PROPERTY_OVERRIDE)
|
||
|
put_char('-', &buf, &bufsize, &needed);
|
||
|
|
||
|
val = ossl_property_name_str(ctx, prop->name_idx);
|
||
|
if (val == NULL)
|
||
|
return 0;
|
||
|
put_str(val, &buf, &bufsize, &needed);
|
||
|
|
||
|
switch (prop->oper) {
|
||
|
case OSSL_PROPERTY_OPER_NE:
|
||
|
put_char('!', &buf, &bufsize, &needed);
|
||
|
/* fall through */
|
||
|
case OSSL_PROPERTY_OPER_EQ:
|
||
|
put_char('=', &buf, &bufsize, &needed);
|
||
|
/* put value */
|
||
|
switch (prop->type) {
|
||
|
case OSSL_PROPERTY_TYPE_STRING:
|
||
|
val = ossl_property_value_str(ctx, prop->v.str_val);
|
||
|
if (val == NULL)
|
||
|
return 0;
|
||
|
put_str(val, &buf, &bufsize, &needed);
|
||
|
break;
|
||
|
|
||
|
case OSSL_PROPERTY_TYPE_NUMBER:
|
||
|
put_num(prop->v.int_val, &buf, &bufsize, &needed);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
/* do nothing */
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
put_char('\0', &buf, &bufsize, &needed);
|
||
|
return needed;
|
||
|
}
|