chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,688 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/conf.h>
#include <ctype.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/buf.h>
#include <openssl/err.h>
#include <openssl/lhash.h>
#include <openssl/mem.h>
#include "../internal.h"
#include "conf_def.h"
#include "internal.h"
static const char kDefaultSectionName[] = "default";
static uint32_t conf_value_hash(const CONF_VALUE *v) {
const uint32_t section_hash = v->section ? OPENSSL_strhash(v->section) : 0;
const uint32_t name_hash = v->name ? OPENSSL_strhash(v->name) : 0;
return (section_hash << 2) ^ name_hash;
}
static int conf_value_cmp(const CONF_VALUE *a, const CONF_VALUE *b) {
int i;
if (a->section != b->section) {
i = strcmp(a->section, b->section);
if (i) {
return i;
}
}
if (a->name != NULL && b->name != NULL) {
return strcmp(a->name, b->name);
} else if (a->name == b->name) {
return 0;
} else {
return (a->name == NULL) ? -1 : 1;
}
}
CONF *NCONF_new(void *method) {
CONF *conf;
if (method != NULL) {
return NULL;
}
conf = OPENSSL_malloc(sizeof(CONF));
if (conf == NULL) {
return NULL;
}
conf->data = lh_CONF_VALUE_new(conf_value_hash, conf_value_cmp);
if (conf->data == NULL) {
OPENSSL_free(conf);
return NULL;
}
return conf;
}
CONF_VALUE *CONF_VALUE_new(void) { return OPENSSL_zalloc(sizeof(CONF_VALUE)); }
static void value_free_contents(CONF_VALUE *value) {
OPENSSL_free(value->section);
if (value->name) {
OPENSSL_free(value->name);
OPENSSL_free(value->value);
} else {
// TODO(davidben): When |value->name| is NULL, |CONF_VALUE| is actually an
// entirely different structure. This is fragile and confusing. Make a
// proper |CONF_SECTION| type that doesn't require this.
sk_CONF_VALUE_free((STACK_OF(CONF_VALUE) *)value->value);
}
}
static void value_free(CONF_VALUE *value) {
if (value != NULL) {
value_free_contents(value);
OPENSSL_free(value);
}
}
static void value_free_arg(CONF_VALUE *value, void *arg) { value_free(value); }
void NCONF_free(CONF *conf) {
if (conf == NULL || conf->data == NULL) {
return;
}
lh_CONF_VALUE_doall_arg(conf->data, value_free_arg, NULL);
lh_CONF_VALUE_free(conf->data);
OPENSSL_free(conf);
}
static CONF_VALUE *NCONF_new_section(const CONF *conf, const char *section) {
STACK_OF(CONF_VALUE) *sk = NULL;
int ok = 0;
CONF_VALUE *v = NULL, *old_value;
sk = sk_CONF_VALUE_new_null();
v = CONF_VALUE_new();
if (sk == NULL || v == NULL) {
goto err;
}
v->section = OPENSSL_strdup(section);
if (v->section == NULL) {
goto err;
}
v->name = NULL;
v->value = (char *)sk;
if (!lh_CONF_VALUE_insert(conf->data, &old_value, v)) {
goto err;
}
value_free(old_value);
ok = 1;
err:
if (!ok) {
sk_CONF_VALUE_free(sk);
OPENSSL_free(v);
v = NULL;
}
return v;
}
static int str_copy(CONF *conf, char *section, char **pto, char *from) {
int q = 0, to = 0, len = 0, r = 0;
char *rrp = NULL, *s = NULL, *cp = NULL, *e = NULL, *rp = NULL, *np = NULL;
const char *p = NULL;
char rr = 0;
char v;
BUF_MEM *buf;
buf = BUF_MEM_new();
if (buf == NULL) {
return 0;
}
len = strlen(from) + 1;
if (!BUF_MEM_grow(buf, len)) {
goto err;
}
for (;;) {
if (IS_QUOTE(conf, *from)) {
q = *from;
from++;
while (!IS_EOF(conf, *from) && (*from != q)) {
if (IS_ESC(conf, *from)) {
from++;
if (IS_EOF(conf, *from)) {
break;
}
}
buf->data[to++] = *(from++);
}
if (*from == q) {
from++;
} else {
OPENSSL_PUT_ERROR(CONF, CONF_R_NO_CLOSE_QUOTE);
goto err;
}
} else if (IS_ESC(conf, *from)) {
from++;
v = *(from++);
if (IS_EOF(conf, v)) {
break;
} else if (v == 'r') {
v = '\r';
} else if (v == 'n') {
v = '\n';
} else if (v == 'b') {
v = '\b';
} else if (v == 't') {
v = '\t';
}
buf->data[to++] = v;
} else if (IS_EOF(conf, *from)) {
break;
} else if (*from == '$') {
size_t newsize = 0;
// try to expand it
rrp = NULL;
s = &(from[1]);
if (*s == '{') {
q = '}';
} else if (*s == '(') {
q = ')';
} else {
q = 0;
}
if (q) {
s++;
}
cp = section;
e = np = s;
while (IS_ALPHA_NUMERIC(conf, *e)) {
e++;
}
if ((e[0] == ':') && (e[1] == ':')) {
cp = np;
rrp = e;
rr = *e;
*rrp = '\0';
e += 2;
np = e;
while (IS_ALPHA_NUMERIC(conf, *e)) {
e++;
}
}
r = *e;
*e = '\0';
rp = e;
if (q) {
if (r != q) {
OPENSSL_PUT_ERROR(CONF, CONF_R_NO_CLOSE_BRACE);
goto err;
}
e++;
}
// So at this point we have
// np which is the start of the name string which is
// '\0' terminated.
// cp which is the start of the section string which is
// '\0' terminated.
// e is the 'next point after'.
// r and rr are the chars replaced by the '\0'
// rp and rrp is where 'r' and 'rr' came from.
p = NCONF_get_string(conf, cp, np);
if (rrp != NULL) {
*rrp = rr;
}
*rp = r;
if (p == NULL) {
OPENSSL_PUT_ERROR(CONF, CONF_R_VARIABLE_HAS_NO_VALUE);
goto err;
}
newsize = strlen(p) + buf->length - (e - from);
if (newsize > UINT16_MAX) {
OPENSSL_PUT_ERROR(CONF, CONF_R_VARIABLE_EXPANSION_TOO_LONG);
goto err;
}
if (!BUF_MEM_grow_clean(buf, newsize)) {
OPENSSL_PUT_ERROR(CONF, ERR_R_MALLOC_FAILURE);
goto err;
}
while (*p) {
buf->data[to++] = *(p++);
}
from = e;
} else {
buf->data[to++] = *(from++);
}
}
buf->data[to] = '\0';
OPENSSL_free(*pto);
*pto = buf->data;
OPENSSL_free(buf);
return 1;
err:
BUF_MEM_free(buf);
return 0;
}
static CONF_VALUE *get_section(const CONF *conf, const char *section) {
CONF_VALUE template;
OPENSSL_memset(&template, 0, sizeof(template));
template.section = (char *)section;
return lh_CONF_VALUE_retrieve(conf->data, &template);
}
const STACK_OF(CONF_VALUE) *NCONF_get_section(const CONF *conf,
const char *section) {
const CONF_VALUE *section_value = get_section(conf, section);
if (section_value == NULL) {
return NULL;
}
return (STACK_OF(CONF_VALUE) *)section_value->value;
}
const char *NCONF_get_string(const CONF *conf, const char *section,
const char *name) {
CONF_VALUE template, *value;
OPENSSL_memset(&template, 0, sizeof(template));
if (section != NULL) {
template.section = (char *)section;
template.name = (char *)name;
value = lh_CONF_VALUE_retrieve(conf->data, &template);
if (value != NULL) {
return value->value;
}
}
char defaultSectionName[] = "default";
template.section = defaultSectionName;
template.name = (char *)name;
value = lh_CONF_VALUE_retrieve(conf->data, &template);
if (value != NULL) {
return value->value;
}
return NULL;
}
static int add_string(const CONF *conf, CONF_VALUE *section,
CONF_VALUE *value) {
STACK_OF(CONF_VALUE) *section_stack = (STACK_OF(CONF_VALUE) *)section->value;
CONF_VALUE *old_value;
value->section = OPENSSL_strdup(section->section);
if (!sk_CONF_VALUE_push(section_stack, value)) {
return 0;
}
if (!lh_CONF_VALUE_insert(conf->data, &old_value, value)) {
return 0;
}
if (old_value != NULL) {
(void)sk_CONF_VALUE_delete_ptr(section_stack, old_value);
value_free(old_value);
}
return 1;
}
static char *eat_ws(CONF *conf, char *p) {
while (IS_WS(conf, *p) && !IS_EOF(conf, *p)) {
p++;
}
return p;
}
#define scan_esc(conf, p) (((IS_EOF((conf), (p)[1])) ? ((p) + 1) : ((p) + 2)))
static char *eat_alpha_numeric(CONF *conf, char *p) {
for (;;) {
if (IS_ESC(conf, *p)) {
p = scan_esc(conf, p);
continue;
}
if (!IS_ALPHA_NUMERIC_PUNCT(conf, *p)) {
return p;
}
p++;
}
}
static char *scan_quote(CONF *conf, char *p) {
int q = *p;
p++;
while (!IS_EOF(conf, *p) && *p != q) {
if (IS_ESC(conf, *p)) {
p++;
if (IS_EOF(conf, *p)) {
return p;
}
}
p++;
}
if (*p == q) {
p++;
}
return p;
}
static void clear_comments(CONF *conf, char *p) {
for (;;) {
if (!IS_WS(conf, *p)) {
break;
}
p++;
}
for (;;) {
if (IS_COMMENT(conf, *p)) {
*p = '\0';
return;
}
if (IS_QUOTE(conf, *p)) {
p = scan_quote(conf, p);
continue;
}
if (IS_ESC(conf, *p)) {
p = scan_esc(conf, p);
continue;
}
if (IS_EOF(conf, *p)) {
return;
} else {
p++;
}
}
}
int NCONF_load_bio(CONF *conf, BIO *in, long *out_error_line) {
static const size_t CONFBUFSIZE = 512;
int bufnum = 0, i, ii;
BUF_MEM *buff = NULL;
char *s, *p, *end;
int again;
long eline = 0;
char btmp[DECIMAL_SIZE(eline) + 1];
CONF_VALUE *v = NULL, *tv;
CONF_VALUE *sv = NULL;
char *section = NULL, *buf;
char *start, *psection, *pname;
if ((buff = BUF_MEM_new()) == NULL) {
OPENSSL_PUT_ERROR(CONF, ERR_R_BUF_LIB);
goto err;
}
section = OPENSSL_strdup(kDefaultSectionName);
if (section == NULL) {
goto err;
}
sv = NCONF_new_section(conf, section);
if (sv == NULL) {
OPENSSL_PUT_ERROR(CONF, CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
goto err;
}
bufnum = 0;
again = 0;
for (;;) {
if (!BUF_MEM_grow(buff, bufnum + CONFBUFSIZE)) {
OPENSSL_PUT_ERROR(CONF, ERR_R_BUF_LIB);
goto err;
}
p = &(buff->data[bufnum]);
*p = '\0';
BIO_gets(in, p, CONFBUFSIZE - 1);
p[CONFBUFSIZE - 1] = '\0';
ii = i = strlen(p);
if (i == 0 && !again) {
break;
}
again = 0;
while (i > 0) {
if ((p[i - 1] != '\r') && (p[i - 1] != '\n')) {
break;
} else {
i--;
}
}
// we removed some trailing stuff so there is a new
// line on the end.
if (ii && i == ii) {
again = 1; // long line
} else {
p[i] = '\0';
eline++; // another input line
}
// we now have a line with trailing \r\n removed
// i is the number of bytes
bufnum += i;
v = NULL;
// check for line continuation
if (bufnum >= 1) {
// If we have bytes and the last char '\\' and
// second last char is not '\\'
p = &(buff->data[bufnum - 1]);
if (IS_ESC(conf, p[0]) && ((bufnum <= 1) || !IS_ESC(conf, p[-1]))) {
bufnum--;
again = 1;
}
}
if (again) {
continue;
}
bufnum = 0;
buf = buff->data;
clear_comments(conf, buf);
s = eat_ws(conf, buf);
if (IS_EOF(conf, *s)) {
continue; // blank line
}
if (*s == '[') {
char *ss;
s++;
start = eat_ws(conf, s);
ss = start;
again:
end = eat_alpha_numeric(conf, ss);
p = eat_ws(conf, end);
if (*p != ']') {
if (*p != '\0' && ss != p) {
ss = p;
goto again;
}
OPENSSL_PUT_ERROR(CONF, CONF_R_MISSING_CLOSE_SQUARE_BRACKET);
goto err;
}
*end = '\0';
if (!str_copy(conf, NULL, &section, start)) {
goto err;
}
if ((sv = get_section(conf, section)) == NULL) {
sv = NCONF_new_section(conf, section);
}
if (sv == NULL) {
OPENSSL_PUT_ERROR(CONF, CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
goto err;
}
continue;
} else {
pname = s;
psection = NULL;
end = eat_alpha_numeric(conf, s);
if ((end[0] == ':') && (end[1] == ':')) {
*end = '\0';
end += 2;
psection = pname;
pname = end;
end = eat_alpha_numeric(conf, end);
}
p = eat_ws(conf, end);
if (*p != '=') {
OPENSSL_PUT_ERROR(CONF, CONF_R_MISSING_EQUAL_SIGN);
goto err;
}
*end = '\0';
p++;
start = eat_ws(conf, p);
while (!IS_EOF(conf, *p)) {
p++;
}
p--;
while ((p != start) && (IS_WS(conf, *p))) {
p--;
}
p++;
*p = '\0';
if (!(v = CONF_VALUE_new())) {
goto err;
}
if (psection == NULL) {
psection = section;
}
v->name = OPENSSL_strdup(pname);
if (v->name == NULL) {
goto err;
}
if (!str_copy(conf, psection, &(v->value), start)) {
goto err;
}
if (strcmp(psection, section) != 0) {
if ((tv = get_section(conf, psection)) == NULL) {
tv = NCONF_new_section(conf, psection);
}
if (tv == NULL) {
OPENSSL_PUT_ERROR(CONF, CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
goto err;
}
} else {
tv = sv;
}
if (add_string(conf, tv, v) == 0) {
goto err;
}
v = NULL;
}
}
BUF_MEM_free(buff);
OPENSSL_free(section);
return 1;
err:
BUF_MEM_free(buff);
OPENSSL_free(section);
if (out_error_line != NULL) {
*out_error_line = eline;
}
snprintf(btmp, sizeof btmp, "%ld", eline);
ERR_add_error_data(2, "line ", btmp);
if (v != NULL) {
OPENSSL_free(v->name);
OPENSSL_free(v->value);
OPENSSL_free(v);
}
return 0;
}
int NCONF_load(CONF *conf, const char *filename, long *out_error_line) {
BIO *in = BIO_new_file(filename, "rb");
int ret;
if (in == NULL) {
OPENSSL_PUT_ERROR(CONF, ERR_R_SYS_LIB);
return 0;
}
ret = NCONF_load_bio(conf, in, out_error_line);
BIO_free(in);
return ret;
}
int CONF_parse_list(const char *list, char sep, int remove_whitespace,
int (*list_cb)(const char *elem, size_t len, void *usr),
void *arg) {
int ret;
const char *lstart, *tmpend, *p;
if (list == NULL) {
OPENSSL_PUT_ERROR(CONF, CONF_R_LIST_CANNOT_BE_NULL);
return 0;
}
lstart = list;
for (;;) {
if (remove_whitespace) {
while (*lstart && OPENSSL_isspace((unsigned char)*lstart)) {
lstart++;
}
}
p = strchr(lstart, sep);
if (p == lstart || !*lstart) {
ret = list_cb(NULL, 0, arg);
} else {
if (p) {
tmpend = p - 1;
} else {
tmpend = lstart + strlen(lstart) - 1;
}
if (remove_whitespace) {
while (OPENSSL_isspace((unsigned char)*tmpend)) {
tmpend--;
}
}
ret = list_cb(lstart, tmpend - lstart + 1, arg);
}
if (ret <= 0) {
return ret;
}
if (p == NULL) {
return 1;
}
lstart = p + 1;
}
}
int CONF_modules_load_file(const char *filename, const char *appname,
unsigned long flags) {
return 1;
}
char *CONF_get1_default_config_file(void) {
return OPENSSL_strdup("No support for Config files in AWS-LC.");
}
void CONF_modules_free(void) {}
void CONF_modules_unload(int all) {}
void CONF_modules_finish(void) {}
void OPENSSL_config(const char *config_name) {}
void OPENSSL_no_config(void) {}

View File

@@ -0,0 +1,68 @@
// Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) All rights reserved.
// SPDX-License-Identifier: Apache-2.0
// This file was historically generated by keysets.pl in OpenSSL.
//
// TODO(davidben): Replace it with something more readable.
#define CONF_NUMBER 1
#define CONF_UPPER 2
#define CONF_LOWER 4
#define CONF_UNDER 256
#define CONF_PUNCTUATION 512
#define CONF_WS 16
#define CONF_ESC 32
#define CONF_QUOTE 64
#define CONF_COMMENT 128
#define CONF_EOF 8
#define CONF_HIGHBIT 4096
#define CONF_ALPHA (CONF_UPPER|CONF_LOWER)
#define CONF_ALPHA_NUMERIC (CONF_ALPHA|CONF_NUMBER|CONF_UNDER)
#define CONF_ALPHA_NUMERIC_PUNCT (CONF_ALPHA|CONF_NUMBER|CONF_UNDER| \
CONF_PUNCTUATION)
#define KEYTYPES(c) CONF_type_default
#define IS_COMMENT(c,a) (KEYTYPES(c)[(a)&0xff]&CONF_COMMENT)
#define IS_EOF(c,a) (KEYTYPES(c)[(a)&0xff]&CONF_EOF)
#define IS_ESC(c,a) (KEYTYPES(c)[(a)&0xff]&CONF_ESC)
#define IS_NUMBER(c,a) (KEYTYPES(c)[(a)&0xff]&CONF_NUMBER)
#define IS_WS(c,a) (KEYTYPES(c)[(a)&0xff]&CONF_WS)
#define IS_ALPHA_NUMERIC(c,a) (KEYTYPES(c)[(a)&0xff]&CONF_ALPHA_NUMERIC)
#define IS_ALPHA_NUMERIC_PUNCT(c,a) \
(KEYTYPES(c)[(a)&0xff]&CONF_ALPHA_NUMERIC_PUNCT)
#define IS_QUOTE(c,a) (KEYTYPES(c)[(a)&0xff]&CONF_QUOTE)
static const unsigned short CONF_type_default[256]={
0x0008,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
0x0000,0x0010,0x0010,0x0000,0x0000,0x0010,0x0000,0x0000,
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
0x0010,0x0200,0x0040,0x0080,0x0000,0x0200,0x0200,0x0040,
0x0000,0x0000,0x0200,0x0200,0x0200,0x0200,0x0200,0x0200,
0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,
0x0001,0x0001,0x0000,0x0200,0x0000,0x0000,0x0000,0x0200,
0x0200,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,
0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,
0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,
0x0002,0x0002,0x0002,0x0000,0x0020,0x0000,0x0200,0x0100,
0x0040,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,
0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,
0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004,
0x0004,0x0004,0x0004,0x0000,0x0200,0x0000,0x0200,0x0000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,
};

View File

@@ -0,0 +1,636 @@
// Copyright (c) 2021, Google Inc.
// SPDX-License-Identifier: ISC
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <openssl/bio.h>
#include <openssl/conf.h>
#include "../test/test_util.h"
#include <gtest/gtest.h>
#include "internal.h"
// A |CONF| is an unordered list of sections, where each section contains an
// ordered list of (name, value) pairs.
using ConfModel =
std::map<std::string, std::vector<std::pair<std::string, std::string>>>;
static void ExpectConfEquals(const CONF *conf, const ConfModel &model) {
// There is always a default section, even if empty. This is an easy mistake
// to make in test data, so test for it.
EXPECT_NE(model.find("default"), model.end())
<< "Model does not have a default section";
size_t total_values = 0;
for (const auto &pair : model) {
const std::string &section = pair.first;
SCOPED_TRACE(section);
const STACK_OF(CONF_VALUE) *values =
NCONF_get_section(conf, section.c_str());
ASSERT_TRUE(values);
total_values += pair.second.size();
EXPECT_EQ(sk_CONF_VALUE_num(values), pair.second.size());
// If the lengths do not match, still compare up to the smaller of the two,
// to aid debugging.
size_t min_len = std::min(sk_CONF_VALUE_num(values), pair.second.size());
for (size_t i = 0; i < min_len; i++) {
SCOPED_TRACE(i);
const std::string &name = pair.second[i].first;
const std::string &value = pair.second[i].second;
const CONF_VALUE *v = sk_CONF_VALUE_value(values, i);
ASSERT_NE(v, nullptr);
EXPECT_EQ(v->section, section);
EXPECT_EQ(v->name, name);
EXPECT_EQ(v->value, value);
const char *str = NCONF_get_string(conf, section.c_str(), name.c_str());
ASSERT_NE(str, nullptr);
EXPECT_EQ(str, value);
if (section == "default") {
// nullptr is interpreted as the default section.
str = NCONF_get_string(conf, nullptr, name.c_str());
ASSERT_NE(str, nullptr);
EXPECT_EQ(str, value);
}
}
}
// Unrecognized sections must return nullptr.
EXPECT_EQ(NCONF_get_section(conf, "must_not_appear_in_tests"), nullptr);
EXPECT_EQ(NCONF_get_string(conf, "must_not_appear_in_tests",
"must_not_appear_in_tests"),
nullptr);
if (!model.empty()) {
// Valid section, invalid name.
EXPECT_EQ(NCONF_get_string(conf, model.begin()->first.c_str(),
"must_not_appear_in_tests"),
nullptr);
if (!model.begin()->second.empty()) {
// Invalid section, matching valid name in default returns value from
// default.
auto key = model.begin()->second.front().first.c_str();
auto default_value = NCONF_get_string(conf, "default", key);
auto retrieved = NCONF_get_string(conf, "must_not_appear_in_tests", key);
if (default_value) {
EXPECT_EQ(Bytes(retrieved), Bytes(default_value));
} else {
EXPECT_EQ(retrieved, nullptr);
}
}
}
// There should not be any other values in |conf|. |conf| currently stores
// both sections and values in the same map.
EXPECT_EQ(lh_CONF_VALUE_num_items(conf->data), total_values + model.size());
}
TEST(ConfTest, Parse) {
const struct {
std::string in;
ConfModel model;
} kTests[] = {
// Test basic parsing.
{
R"(# Comment
key=value
[section_name]
key=value2
)",
{
{"default", {{"key", "value"}}},
{"section_name", {{"key", "value2"}}},
},
},
// If a section is listed multiple times, keys add to the existing one.
{
R"(key1 = value1
[section1]
key2 = value2
[section2]
key3 = value3
[default]
key4 = value4
[section1]
key5 = value5
)",
{
{"default", {{"key1", "value1"}, {"key4", "value4"}}},
{"section1", {{"key2", "value2"}, {"key5", "value5"}}},
{"section2", {{"key3", "value3"}}},
},
},
// Although the CONF parser internally uses a buffer size of 512 bytes to
// read one line, it detects truncation and is able to parse long lines.
{
std::string(1000, 'a') + " = " + std::string(1000, 'b') + "\n",
{
{"default", {{std::string(1000, 'a'), std::string(1000, 'b')}}},
},
},
// Trailing backslashes are line continations.
{
"key=\\\nvalue\nkey2=foo\\\nbar=baz",
{
{"default", {{"key", "value"}, {"key2", "foobar=baz"}}},
},
},
// To be a line continuation, it must be at the end of the line.
{
"key=\\\nvalue\nkey2=foo\\ \nbar=baz",
{
{"default", {{"key", "value"}, {"key2", "foo"}, {"bar", "baz"}}},
},
},
// A line continuation without any following line is ignored.
{
"key=value\\",
{
{"default", {{"key", "value"}}},
},
},
// Values may have embedded whitespace, but leading and trailing
// whitespace is dropped.
{
"key = \t foo \t\t\tbar \t ",
{
{"default", {{"key", "foo \t\t\tbar"}}},
},
},
// Empty sections still end up in the file.
{
"[section1]\n[section2]\n[section3]\n",
{
{"default", {}},
{"section1", {}},
{"section2", {}},
{"section3", {}},
},
},
// Section names can contain spaces and punctuation.
{
"[This! Is. A? Section;]\nkey = value",
{
{"default", {}},
{"This! Is. A? Section;", {{"key", "value"}}},
},
},
// Trailing data after a section line is ignored.
{
"[section] key = value\nkey2 = value2\n",
{
{"default", {}},
{"section", {{"key2", "value2"}}},
},
},
// Comments may appear within a line. Escapes and quotes, however,
// suppress the comment character.
{
R"(
key1 = # comment
key2 = "# not a comment"
key3 = '# not a comment'
key4 = `# not a comment`
key5 = \# not a comment
)",
{
{"default",
{
{"key1", ""},
{"key2", "# not a comment"},
{"key3", "# not a comment"},
{"key4", "# not a comment"},
{"key5", "# not a comment"},
}},
},
},
// Quotes may appear in the middle of a string. Inside quotes, escape
// sequences like \n are not evaluated. \X always evaluates to X.
{
R"(
key1 = mix "of" 'different' `quotes`
key2 = "`'"
key3 = "\r\n\b\t\""
key4 = '\r\n\b\t\''
key5 = `\r\n\b\t\``
)",
{
{"default",
{
{"key1", "mix of different quotes"},
{"key2", "`'"},
{"key3", "rnbt\""},
{"key4", "rnbt'"},
{"key5", "rnbt`"},
}},
},
},
// Outside quotes, escape sequences like \n are evaluated. Unknown escapes
// turn into the character.
{
R"(
key = \r\n\b\t\"\'\`\z
)",
{
{"default",
{
{"key", "\r\n\b\t\"'`z"},
}},
},
},
// Escapes (but not quoting) work inside section names.
{
"[section\\ name]\nkey = value\n",
{
{"default", {}},
{"section name", {{"key", "value"}}},
},
},
// Escapes (but not quoting) are skipped over in key names, but they are
// left unevaluated. This is probably a bug.
{
"key\\ name = value\n",
{
{"default", {{"key\\ name", "value"}}},
},
},
// Keys can specify sections explicitly with ::.
{
R"(
[section1]
default::key1 = value1
section1::key2 = value2
section2::key3 = value3
section1::key4 = value4
section2::key5 = value5
default::key6 = value6
key7 = value7 # section1
)",
{
{"default", {{"key1", "value1"}, {"key6", "value6"}}},
{"section1",
{{"key2", "value2"}, {"key4", "value4"}, {"key7", "value7"}}},
{"section2", {{"key3", "value3"}, {"key5", "value5"}}},
},
},
// Punctuation is allowed in key names.
{
"key.1 = value\n",
{
{"default", {{"key.1", "value"}}},
},
},
// Variable references have been readded.
{
R"(
key1 = value1
key2 = $key1
)",
{
{
"default",
{{"key1", "value1"}, {"key2", "value1"}},
},
},
},
// Variable expansion with curly braces ${foo}
{
R"(
key1 = value1
key2 = ${key1}
)",
{
{
"default",
{{"key1", "value1"}, {"key2", "value1"}},
},
},
},
// Variable expansion with parentheses $(foo)
{
R"(
key1 = value1
key2 = $(key1)
)",
{
{
"default",
{{"key1", "value1"}, {"key2", "value1"}},
},
},
},
// Variable expansion with mixed content (prefix and suffix)
{
R"(
key1 = middle
key2 = prefix_${key1}_suffix
)",
{
{
"default",
{{"key1", "middle"}, {"key2", "prefix_middle_suffix"}},
},
},
},
// Multiple variable expansions in one value
{
R"(
key1 = hello
key2 = world
key3 = $key1 $key2
)",
{
{
"default",
{{"key1", "hello"}, {"key2", "world"}, {"key3", "hello world"}},
},
},
},
// Chained variable expansion
{
R"(
key1 = base
key2 = $key1
key3 = $key2
)",
{
{
"default",
{{"key1", "base"}, {"key2", "base"}, {"key3", "base"}},
},
},
},
// Cross-section variable reference with ${section::key}
{
R"(
[section1]
key1 = from_section1
[section2]
key2 = ${section1::key1}
)",
{
{"default", {}},
{"section1", {{"key1", "from_section1"}}},
{"section2", {{"key2", "from_section1"}}},
},
},
// Cross-section variable reference with $section::key (no braces)
{
R"(
[section1]
key1 = from_section1
[section2]
key2 = $section1::key1
)",
{
{"default", {}},
{"section1", {{"key1", "from_section1"}}},
{"section2", {{"key2", "from_section1"}}},
},
},
// Cross-section reference to default section
{
R"(
key1 = from_default
[section1]
key2 = ${default::key1}
)",
{
{"default", {{"key1", "from_default"}}},
{"section1", {{"key2", "from_default"}}},
},
},
// Variable name with underscore
{
R"(
key_with_underscore = value1
key2 = $key_with_underscore
)",
{
{
"default",
{{"key_with_underscore", "value1"}, {"key2", "value1"}},
},
},
},
// Variable name with numbers
{
R"(
key123 = value1
key2 = $key123
)",
{
{
"default",
{{"key123", "value1"}, {"key2", "value1"}},
},
},
},
// Variable at start, middle, and end
{
R"(
v = X
start = $v is here
middle = here $v is
end = here is $v
)",
{
{
"default",
{{"v", "X"},
{"start", "X is here"},
{"middle", "here X is"},
{"end", "here is X"}},
},
},
},
// Variable referencing same section implicitly
{
R"(
[mysection]
base = myvalue
derived = $base
)",
{
{"default", {}},
{"mysection", {{"base", "myvalue"}, {"derived", "myvalue"}}},
},
},
// Variable reference with parentheses and cross-section
{
R"(
[section1]
key1 = paren_value
[section2]
key2 = $(section1::key1)
)",
{
{"default", {}},
{"section1", {{"key1", "paren_value"}}},
{"section2", {{"key2", "paren_value"}}},
},
},
// Empty variable expansion (variable with empty value)
{
R"(
empty =
key2 = prefix${empty}suffix
)",
{
{
"default",
{{"empty", ""}, {"key2", "prefixsuffix"}},
},
},
},
};
for (const auto &t : kTests) {
SCOPED_TRACE(t.in);
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(t.in.data(), t.in.size()));
ASSERT_TRUE(bio);
bssl::UniquePtr<CONF> conf(NCONF_new(nullptr));
ASSERT_TRUE(conf);
ASSERT_TRUE(NCONF_load_bio(conf.get(), bio.get(), nullptr));
ExpectConfEquals(conf.get(), t.model);
}
const char *kInvalidTests[] = {
// Missing equals sign.
"key",
// Unterminated section heading.
"[section",
// Section names can only contain alphanumeric characters, punctuation,
// and escapes. Quotes are not punctuation.
"[\"section\"]",
// Keys can only contain alphanumeric characters, punctuaion, and escapes.
"key name = value",
"\"key\" = value",
// Variable expansion: undefined variable.
"key = $undefined_var",
// Variable expansion: missing closing brace.
"key = ${foo",
// Variable expansion: missing closing parenthesis.
"key = $(foo",
// Variable expansion: undefined cross-section variable.
"key = ${nonexistent_section::key}",
// Unterminated quotes (CONF_R_NO_CLOSE_QUOTE).
"key = \"unterminated",
"key = 'unterminated",
"key = `unterminated",
};
for (const auto &t : kInvalidTests) {
SCOPED_TRACE(t);
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(t, strlen(t)));
ASSERT_TRUE(bio);
bssl::UniquePtr<CONF> conf(NCONF_new(nullptr));
ASSERT_TRUE(conf);
EXPECT_FALSE(NCONF_load_bio(conf.get(), bio.get(), nullptr));
}
}
TEST(ConfTest, ParseList) {
const struct {
const char *list;
char sep;
bool remove_whitespace;
std::vector<std::string> expected;
} kTests[] = {
{"", ',', /*remove_whitespace=*/0, {""}},
{"", ',', /*remove_whitespace=*/1, {""}},
{" ", ',', /*remove_whitespace=*/0, {" "}},
{" ", ',', /*remove_whitespace=*/1, {""}},
{"hello world", ',', /*remove_whitespace=*/0, {"hello world"}},
{"hello world", ',', /*remove_whitespace=*/1, {"hello world"}},
{" hello world ", ',', /*remove_whitespace=*/0, {" hello world "}},
{" hello world ", ',', /*remove_whitespace=*/1, {"hello world"}},
{"hello,world", ',', /*remove_whitespace=*/0, {"hello", "world"}},
{"hello,world", ',', /*remove_whitespace=*/1, {"hello", "world"}},
{"hello,,world", ',', /*remove_whitespace=*/0, {"hello", "", "world"}},
{"hello,,world", ',', /*remove_whitespace=*/1, {"hello", "", "world"}},
{"\tab cd , , ef gh ",
',',
/*remove_whitespace=*/0,
{"\tab cd ", " ", " ef gh "}},
{"\tab cd , , ef gh ",
',',
/*remove_whitespace=*/1,
{"ab cd", "", "ef gh"}},
};
for (const auto& t : kTests) {
SCOPED_TRACE(t.list);
SCOPED_TRACE(t.sep);
SCOPED_TRACE(t.remove_whitespace);
std::vector<std::string> result;
auto append_to_vector = [](const char *elem, size_t len, void *arg) -> int {
auto *vec = static_cast<std::vector<std::string> *>(arg);
vec->push_back(std::string(elem, len));
return 1;
};
ASSERT_TRUE(CONF_parse_list(t.list, t.sep, t.remove_whitespace,
append_to_vector, &result));
EXPECT_EQ(result, t.expected);
}
}
TEST(ConfTest, NoopString) {
bssl::UniquePtr<char> string(CONF_get1_default_config_file());
EXPECT_STREQ("No support for Config files in AWS-LC.", string.get());
}

View File

@@ -0,0 +1,36 @@
// Copyright (c) 2015, Google Inc.
// SPDX-License-Identifier: ISC
#ifndef OPENSSL_HEADER_CRYPTO_CONF_INTERNAL_H
#define OPENSSL_HEADER_CRYPTO_CONF_INTERNAL_H
#include <openssl/base.h>
#include "../lhash/internal.h"
#if defined(__cplusplus)
extern "C" {
#endif
DEFINE_LHASH_OF(CONF_VALUE)
// CONF_VALUE_new returns a freshly allocated and zeroed |CONF_VALUE|.
CONF_VALUE *CONF_VALUE_new(void);
// CONF_parse_list takes a list separated by 'sep' and calls |list_cb| giving
// the start and length of each member, optionally stripping leading and
// trailing whitespace. This can be used to parse comma separated lists for
// example. If |list_cb| returns <= 0, then the iteration is halted and that
// value is returned immediately. Otherwise it returns one. Note that |list_cb|
// may be called on an empty member.
OPENSSL_EXPORT int CONF_parse_list(
const char *list, char sep, int remove_whitespace,
int (*list_cb)(const char *elem, size_t len, void *usr), void *arg);
#if defined(__cplusplus)
} // extern C
#endif
#endif // OPENSSL_HEADER_CRYPTO_CONF_INTERNAL_H