Compare commits

..

7 commits

Author SHA1 Message Date
32f31fe6d6 Merge pull request 'Add the DbOperationArgs set of functions, and fix bug with LMDB' (#62) from lda/Cytoplasm:db-args into master
Reviewed-on: Telodendria/Cytoplasm#62
2024-11-10 18:09:05 +00:00
lda
f3838e775c Merge branch 'master' into db-args 2024-11-09 16:03:20 +00:00
LDA
100d61050f [ADD/FIX] Add DbOPERATIONArgs functions, fix LMDB 2024-11-09 16:59:49 +01:00
b4841fffaa Merge pull request 'Add a function to decode a JSON value directly.' (#60) from lda/Cytoplasm:raw-types into master
Reviewed-on: Telodendria/Cytoplasm#60
2024-10-26 18:49:52 +00:00
LDA
39e81139f0 [ADD] Add JsonValueDecode to decode a simple value 2024-10-25 18:45:54 +02:00
8df5f0f1c1 Merge pull request 'Make Memory API more friendly with alignment' (#59) from lda/Cytoplasm:alignment into master
Reviewed-on: Telodendria/Cytoplasm#59
2024-10-24 11:57:50 +00:00
LDA
708c5daad9 [FIX] Fix memory alignment issues
Some architectures(DEC Alpha as a main outlier, but x86 may behave that
way by setting flags) raise traps on unaligned operations, which can be
either costly(having to talk to the kernel, which may have to emulate
the read) or could cause program termination.

Also adds a basic memory interval for checking if a pointer has any
business living within the heap. Most systems separate those anyways so
it avoids doing potentially dangerous operations.
2024-10-24 11:41:33 +02:00
15 changed files with 198 additions and 476 deletions

View file

@ -5,20 +5,6 @@ Cytoplasm. It is intended to be updated with every commit that makes a user-faci
change worth reporting in the change log. As such, it changes frequently between change worth reporting in the change log. As such, it changes frequently between
releases. Final change log entries are published as [Releases](releases). releases. Final change log entries are published as [Releases](releases).
## v0.5.0
**TO BE WRITTEN**
### Breaking Changes
- `ShaToHex` now requires what hash was used.
### New Features
- LMDB support is available with the `--with-lmdb` flag.
- MbedTLS summort is available with the `--with-mbed` flag. Please note that Cytoplasm
programs using it must be used with the `CYTO_TLS_CA` environment defined to map to a
valid CA file, and that the `CYTO_TLS_SEED` flag can be set to use a seedfile, increasing
entropy on systems where it is unavailable.
## v0.4.1 ## v0.4.1
Cytoplasm is now a C99 library! Upgrading from C89 to C99 makes Cytoplasm more portable Cytoplasm is now a C99 library! Upgrading from C89 to C99 makes Cytoplasm more portable
@ -35,6 +21,8 @@ are not even initialized to a default value.
- Make `HttpRouter` decode path parts before matching them on regular expressions. - Make `HttpRouter` decode path parts before matching them on regular expressions.
- Fixed a bug in `ArraySort()` that would crash programs if the passed array has no - Fixed a bug in `ArraySort()` that would crash programs if the passed array has no
elements. elements.
- Adds `Db[OP]Args` functions that are equivalent to their `Db[OP]` counter parts, but
uses an array of string instead of variadic arguments.
## v0.4.0 ## v0.4.0

View file

@ -21,7 +21,6 @@ Cytoplasm aims to have zero software dependencies beyond what is mandated by POS
- OpenSSL - OpenSSL
- LibreSSL - LibreSSL
- MbedTLS (requires the `CYTO_TLS_CA` environment variables for programs built with it)
If TLS support is not enabled, all APIs that use it should fall back to non-TLS behavior in a sensible manner. For example, if TLS support is not enabled, then the HTTP client API will simply return an error if a TLS connection is requested. If TLS support is not enabled, all APIs that use it should fall back to non-TLS behavior in a sensible manner. For example, if TLS support is not enabled, then the HTTP client API will simply return an error if a TLS connection is requested.
@ -51,7 +50,7 @@ You can also run `make install` as `root` to install Cytoplasm to the system. Th
The `configure` script has a number of optional flags, which are as follows: The `configure` script has a number of optional flags, which are as follows:
- `--with-(openssl|libressl|mbed)`: Select the TLS implementation to use. OpenSSL is selected by default. - `--with-(openssl|libressl)`: Select the TLS implementation to use. OpenSSL is selected by default.
- `--disable-tls`: Disable TLS altogether. - `--disable-tls`: Disable TLS altogether.
- `--prefix=<path>`: Set the install prefix to set by default in the `Makefile`. This defaults to `/usr/local`, which should be appropriate for most Unix-like systems. - `--prefix=<path>`: Set the install prefix to set by default in the `Makefile`. This defaults to `/usr/local`, which should be appropriate for most Unix-like systems.
- `--(enable|disable)-debug`: Control whether or not to enable debug mode. This sets the optimization level to 0 and builds with debug symbols. Useful for running with a debugger. - `--(enable|disable)-debug`: Control whether or not to enable debug mode. This sets the optimization level to 0 and builds with debug symbols. Useful for running with a debugger.
@ -114,4 +113,4 @@ Note that both arguments to Main may be treated like any other Cytoplasm array o
All of the code and documentation for Cytoplasm is licensed under the same license as Telodendria itself. Please refer to [Telodendria &rightarrow; License](/Telodendria/Telodendria#license) for details. All of the code and documentation for Cytoplasm is licensed under the same license as Telodendria itself. Please refer to [Telodendria &rightarrow; License](/Telodendria/Telodendria#license) for details.
The Cytoplasm logo was designed by [Tobskep](https://tobskep.com) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0](https://creativecommons.org/licenses/by-sa/4.0/) license. The Cytoplasm logo was designed by [Tobskep](https://tobskep.com) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0](https://creativecommons.org/licenses/by-sa/4.0/) license.

4
configure vendored
View file

@ -76,10 +76,6 @@ for arg in $SCRIPT_ARGS; do
TLS_IMPL="TLS_LIBRESSL" TLS_IMPL="TLS_LIBRESSL"
TLS_LIBS="-ltls -lcrypto -lssl" TLS_LIBS="-ltls -lcrypto -lssl"
;; ;;
--with-mbed)
TLS_IMPL="TLS_MBEDTLS"
TLS_LIBS="-lmbedtls -lmbedx509 -lmbedcrypto"
;;
--disable-tls) --disable-tls)
TLS_IMPL="" TLS_IMPL=""
TLS_LIBS="" TLS_LIBS=""

View file

@ -119,6 +119,13 @@ extern void DbMaxCacheSet(Db *, size_t);
*/ */
extern DbRef * DbCreate(Db *, size_t,...); extern DbRef * DbCreate(Db *, size_t,...);
/**
* Behaves like
* .Fn DbCreate ,
* but with an array of strings instead of variadic arguments.
*/
extern DbRef * DbCreateArgs(Db *, Array *);
/** /**
* Lock an existing object in the database. This function will fail * Lock an existing object in the database. This function will fail
* if the object does not exist. It takes a variable number of C * if the object does not exist. It takes a variable number of C
@ -129,6 +136,13 @@ extern DbRef * DbCreate(Db *, size_t,...);
*/ */
extern DbRef * DbLock(Db *, size_t,...); extern DbRef * DbLock(Db *, size_t,...);
/**
* Behaves like
* .Fn DbLock ,
* but with an array of strings instead of variadic arguments.
*/
extern DbRef * DbLockArgs(Db *, Array *);
/** /**
* Behaves like * Behaves like
* .Fn DbLock , * .Fn DbLock ,
@ -138,6 +152,13 @@ extern DbRef * DbLock(Db *, size_t,...);
*/ */
extern DbRef * DbLockIntent(Db *, DbHint, size_t,...); extern DbRef * DbLockIntent(Db *, DbHint, size_t,...);
/**
* Behaves like
* .Fn DbLockIntent ,
* but with an array of strings instead of variadic arguments.
*/
extern DbRef * DbLockIntentArgs(Db *, DbHint, Array *);
/** /**
* Immediately and permanently remove an object from the database. * Immediately and permanently remove an object from the database.
* This function assumes the object is not locked, otherwise undefined * This function assumes the object is not locked, otherwise undefined
@ -145,6 +166,13 @@ extern DbRef * DbLockIntent(Db *, DbHint, size_t,...);
*/ */
extern bool DbDelete(Db *, size_t,...); extern bool DbDelete(Db *, size_t,...);
/**
* Behaves like
* .Fn DbDelete ,
* but with an array of strings instead of variadic arguments.
*/
extern bool DbDeleteArgs(Db *, Array *);
/** /**
* Unlock an object and return it back to the database. This function * Unlock an object and return it back to the database. This function
* immediately syncs the object to the filesystem. The cache is a * immediately syncs the object to the filesystem. The cache is a
@ -162,6 +190,13 @@ extern bool DbUnlock(Db *, DbRef *);
*/ */
extern bool DbExists(Db *, size_t,...); extern bool DbExists(Db *, size_t,...);
/**
* Behaves like
* .Fn DbExists ,
* but with an array of strings instead of variadic arguments.
*/
extern bool DbExistsArgs(Db *, Array *);
/** /**
* List all of the objects at a given path. Unlike the other varargs * List all of the objects at a given path. Unlike the other varargs
* functions, this one does not take a path to a specific object; it * functions, this one does not take a path to a specific object; it
@ -174,6 +209,13 @@ extern bool DbExists(Db *, size_t,...);
*/ */
extern Array * DbList(Db *, size_t,...); extern Array * DbList(Db *, size_t,...);
/**
* Behaves like
* .Fn DbList ,
* but with an array of strings instead of variadic arguments.
*/
extern Array * DbListArgs(Db *, Array *);
/** /**
* Free the list returned by * Free the list returned by
* .Fn DbListFree . * .Fn DbListFree .

View file

@ -297,6 +297,12 @@ extern size_t JsonEncode(HashMap *, Stream *, int);
*/ */
extern HashMap * JsonDecode(Stream *); extern HashMap * JsonDecode(Stream *);
/**
* Decodes a JSON value from thr current input strram and parse it
* into a JsonValue.
*/
extern JsonValue * JsonValueDecode(Stream *);
/** /**
* A convenience function that allows the caller to retrieve and * A convenience function that allows the caller to retrieve and
* arbitrarily deep keys within a JSON object. It takes a root JSON * arbitrarily deep keys within a JSON object. It takes a root JSON

View file

@ -50,7 +50,6 @@
#define TLS_LIBRESSL 2 #define TLS_LIBRESSL 2
#define TLS_OPENSSL 3 #define TLS_OPENSSL 3
#define TLS_MBEDTLS 4
/** /**
* Create a new TLS client stream using the given file descriptor and * Create a new TLS client stream using the given file descriptor and

View file

@ -262,6 +262,16 @@ DbClose(Db * db)
Free(db); Free(db);
} }
DbRef *
DbCreateArgs(Db * db, Array *args)
{
if (!args || !db || !db->create)
{
return NULL;
}
return db->create(db, args);
}
DbRef * DbRef *
DbCreate(Db * db, size_t nArgs,...) DbCreate(Db * db, size_t nArgs,...)
{ {
@ -283,12 +293,22 @@ DbCreate(Db * db, size_t nArgs,...)
return NULL; return NULL;
} }
ret = db->create(db, args); ret = DbCreateArgs(db, args);
ArrayFree(args); ArrayFree(args);
return ret; return ret;
} }
bool
DbDeleteArgs(Db * db, Array *args)
{
if (!args || !db || !db->delete)
{
return false;
}
return db->delete(db, args);
}
bool bool
DbDelete(Db * db, size_t nArgs,...) DbDelete(Db * db, size_t nArgs,...)
{ {
@ -305,12 +325,22 @@ DbDelete(Db * db, size_t nArgs,...)
args = ArrayFromVarArgs(nArgs, ap); args = ArrayFromVarArgs(nArgs, ap);
va_end(ap); va_end(ap);
ret = db->delete(db, args); ret = DbDeleteArgs(db, args);
ArrayFree(args); ArrayFree(args);
return ret; return ret;
} }
DbRef *
DbLockArgs(Db * db, Array *args)
{
if (!args || !db || !db->lockFunc)
{
return NULL;
}
return db->lockFunc(db, DB_HINT_WRITE, args);
}
DbRef * DbRef *
DbLock(Db * db, size_t nArgs,...) DbLock(Db * db, size_t nArgs,...)
{ {
@ -318,6 +348,11 @@ DbLock(Db * db, size_t nArgs,...)
Array *args; Array *args;
DbRef *ret; DbRef *ret;
if (!db)
{
return NULL;
}
va_start(ap, nArgs); va_start(ap, nArgs);
args = ArrayFromVarArgs(nArgs, ap); args = ArrayFromVarArgs(nArgs, ap);
va_end(ap); va_end(ap);
@ -327,13 +362,22 @@ DbLock(Db * db, size_t nArgs,...)
return NULL; return NULL;
} }
ret = db->lockFunc(db, DB_HINT_WRITE, args); ret = DbLockArgs(db, args);
ArrayFree(args); ArrayFree(args);
return ret; return ret;
} }
DbRef *
DbLockIntentArgs(Db * db, DbHint hint, Array *args)
{
if (!db || !args || !db->lockFunc)
{
return NULL;
}
return db->lockFunc(db, hint, args);
}
DbRef * DbRef *
DbLockIntent(Db * db, DbHint hint, size_t nArgs,...) DbLockIntent(Db * db, DbHint hint, size_t nArgs,...)
{ {
@ -350,7 +394,7 @@ DbLockIntent(Db * db, DbHint hint, size_t nArgs,...)
return NULL; return NULL;
} }
ret = db->lockFunc(db, hint, args); ret = DbLockIntentArgs(db, hint, args);
ArrayFree(args); ArrayFree(args);
@ -368,6 +412,16 @@ DbUnlock(Db * db, DbRef * ref)
return db->unlock(db, ref); return db->unlock(db, ref);
} }
bool
DbExistsArgs(Db * db, Array *args)
{
if (!args || !db || !db->exists)
{
return false;
}
return db->exists(db, args);
}
bool bool
DbExists(Db * db, size_t nArgs,...) DbExists(Db * db, size_t nArgs,...)
{ {
@ -388,12 +442,21 @@ DbExists(Db * db, size_t nArgs,...)
return false; return false;
} }
ret = db->exists(db, args); ret = DbExistsArgs(db, args);
ArrayFree(args); ArrayFree(args);
return ret; return ret;
} }
Array *
DbListArgs(Db *db, Array *args)
{
if (!db || !args || !db->list)
{
return NULL;
}
return db->list(db, args);
}
Array * Array *
DbList(Db * db, size_t nArgs,...) DbList(Db * db, size_t nArgs,...)
{ {
@ -410,7 +473,7 @@ DbList(Db * db, size_t nArgs,...)
path = ArrayFromVarArgs(nArgs, ap); path = ArrayFromVarArgs(nArgs, ap);
va_end(ap); va_end(ap);
result = db->list(db, path); result = DbListArgs(db, path);
ArrayFree(path); ArrayFree(path);
return result; return result;

View file

@ -54,7 +54,8 @@ DbDirName(FlatDb * db, Array * args, size_t strip)
{ {
char *tmp; char *tmp;
char *sanitise = StrDuplicate(ArrayGet(args, i)); char *sanitise = StrDuplicate(ArrayGet(args, i));
for (j = 0; j < strlen(sanitise); j++) size_t len = strlen(sanitise);
for (j = 0; j < len; j++)
{ {
sanitise[j] = DbSanitiseChar(sanitise[j]); sanitise[j] = DbSanitiseChar(sanitise[j]);
} }

View file

@ -128,7 +128,9 @@ LMDBKeyHead(MDB_val key)
return NULL; return NULL;
} }
while ((void *) (end - 1) >= key.mv_data && *(end - 1)) /* Doing >= will lead to cases where it is sent straight to the
* start. Don't do that. */
while ((void *) (end - 1) > key.mv_data && *(end - 1))
{ {
end--; end--;
} }

View file

@ -1348,6 +1348,25 @@ JsonDecode(Stream * stream)
return result; return result;
} }
JsonValue *
JsonValueDecode(Stream *stream)
{
JsonValue *result;
JsonParserState state;
state.stream = stream;
state.token = NULL;
JsonTokenSeek(&state);
result = JsonDecodeValue(&state);
if (state.token)
{
Free(state.token);
}
return result;
}
JsonValue * JsonValue *
JsonGet(HashMap * json, size_t nArgs,...) JsonGet(HashMap * json, size_t nArgs,...)

View file

@ -42,26 +42,30 @@
#define MEMORY_FILE_SIZE 256 #define MEMORY_FILE_SIZE 256
#define MEM_BOUND_TYPE uint64_t
#define MEM_BOUND 0xDEADBEEFBEEFDEAD
#define MEM_MAGIC 0xDEADBEEFDEADBEEF
struct MemoryInfo struct MemoryInfo
{ {
uint64_t magic; uint64_t magic;
size_t size; size_t size;
size_t unalignedSize;
char file[MEMORY_FILE_SIZE]; char file[MEMORY_FILE_SIZE];
int line; int line;
void *pointer; void *pointer;
MemoryInfo *prev; MemoryInfo *prev;
MemoryInfo *next; MemoryInfo *next;
MEM_BOUND_TYPE leftBoundary;
}; };
#define MEM_BOUND_TYPE uint32_t #define MEM_SIZE_ACTUAL(x) (MemoryAlignBoundary((x) * sizeof(uint8_t)) + sizeof(MEM_BOUND_TYPE))
#define MEM_BOUND 0xDEADBEEF #define MEM_START_BOUNDARY(info) (info->leftBoundary)
#define MEM_MAGIC 0xDEADBEEFDEADBEEF #define MEM_END_BOUNDARY(info) (*(((MEM_BOUND_TYPE *) (((uint8_t *) info->pointer) + info->size)) - 1))
#define MEM_BOUND_LOWER(p) *((MEM_BOUND_TYPE *) p)
#define MEM_BOUND_UPPER(p, x) *(((MEM_BOUND_TYPE *) (((uint8_t *) p) + x)) + 1)
#define MEM_SIZE_ACTUAL(x) (((x) * sizeof(uint8_t)) + (2 * sizeof(MEM_BOUND_TYPE)))
static pthread_mutex_t lock; static pthread_mutex_t lock;
static void (*hook) (MemoryAction, MemoryInfo *, void *) = MemoryDefaultHook; static void (*hook) (MemoryAction, MemoryInfo *, void *) = MemoryDefaultHook;
@ -71,6 +75,19 @@ static size_t allocationsLen = 0;
static MemoryInfo *allocationTail = NULL; static MemoryInfo *allocationTail = NULL;
/* Simple range of "plausible" boundaries for heap, serving as an extra
* check */
static void *heapStart, *heapEnd;
static size_t MemoryAlignBoundary(size_t size)
{
size_t boundSize = sizeof(MEM_BOUND_TYPE);
size_t remainder = size % boundSize;
size_t closest = size / boundSize + !!remainder;
return closest * boundSize;
}
int int
MemoryRuntimeInit(void) MemoryRuntimeInit(void)
{ {
@ -85,6 +102,8 @@ MemoryRuntimeInit(void)
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
ret = pthread_mutex_init(&lock, &attr); ret = pthread_mutex_init(&lock, &attr);
pthread_mutexattr_destroy(&attr); pthread_mutexattr_destroy(&attr);
heapStart = NULL;
heapEnd = NULL;
ret = (ret == 0); ret = (ret == 0);
@ -110,6 +129,15 @@ MemoryInsert(MemoryInfo * a)
a->prev = allocationTail; a->prev = allocationTail;
a->magic = MEM_MAGIC; a->magic = MEM_MAGIC;
if (!heapStart || heapStart > (void *) a)
{
heapStart = a;
}
if (!heapEnd || heapEnd < (void *) a)
{
heapEnd = a;
}
allocationTail = a; allocationTail = a;
allocationsLen++; allocationsLen++;
@ -142,8 +170,9 @@ MemoryDelete(MemoryInfo * a)
static int static int
MemoryCheck(MemoryInfo * a) MemoryCheck(MemoryInfo * a)
{ {
if (MEM_BOUND_LOWER(a->pointer) != MEM_BOUND || if (MEM_START_BOUNDARY(a) != MEM_BOUND ||
MEM_BOUND_UPPER(a->pointer, a->size - (2 * sizeof(MEM_BOUND_TYPE))) != MEM_BOUND) a->magic != MEM_MAGIC ||
MEM_END_BOUNDARY(a) != MEM_BOUND)
{ {
if (hook) if (hook)
{ {
@ -174,13 +203,14 @@ MemoryAllocate(size_t size, const char *file, int line)
p = a + 1; p = a + 1;
memset(p, 0, MEM_SIZE_ACTUAL(size)); memset(p, 0, MEM_SIZE_ACTUAL(size));
MEM_BOUND_LOWER(p) = MEM_BOUND;
MEM_BOUND_UPPER(p, size) = MEM_BOUND;
a->size = MEM_SIZE_ACTUAL(size); a->size = MEM_SIZE_ACTUAL(size);
a->unalignedSize = size;
strncpy(a->file, file, MEMORY_FILE_SIZE - 1); strncpy(a->file, file, MEMORY_FILE_SIZE - 1);
a->line = line; a->line = line;
a->pointer = p; a->pointer = p;
MEM_START_BOUNDARY(a) = MEM_BOUND;
MEM_END_BOUNDARY(a) = MEM_BOUND;
if (!MemoryInsert(a)) if (!MemoryInsert(a))
{ {
@ -195,7 +225,7 @@ MemoryAllocate(size_t size, const char *file, int line)
} }
pthread_mutex_unlock(&lock); pthread_mutex_unlock(&lock);
return ((MEM_BOUND_TYPE *) p) + 1; return p;
} }
void * void *
@ -220,6 +250,7 @@ MemoryReallocate(void *p, size_t size, const char *file, int line)
if (new) if (new)
{ {
a = new; a = new;
a->unalignedSize = size;
a->size = MEM_SIZE_ACTUAL(size); a->size = MEM_SIZE_ACTUAL(size);
strncpy(a->file, file, MEMORY_FILE_SIZE - 1); strncpy(a->file, file, MEMORY_FILE_SIZE - 1);
a->line = line; a->line = line;
@ -228,8 +259,8 @@ MemoryReallocate(void *p, size_t size, const char *file, int line)
a->pointer = a + 1; a->pointer = a + 1;
MemoryInsert(a); MemoryInsert(a);
MEM_BOUND_LOWER(a->pointer) = MEM_BOUND; MEM_START_BOUNDARY(a) = MEM_BOUND;
MEM_BOUND_UPPER(a->pointer, size) = MEM_BOUND; MEM_END_BOUNDARY(a) = MEM_BOUND;
if (hook) if (hook)
{ {
@ -253,7 +284,7 @@ MemoryReallocate(void *p, size_t size, const char *file, int line)
} }
} }
return ((MEM_BOUND_TYPE *) a->pointer) + 1; return a->pointer;
} }
void void
@ -344,8 +375,12 @@ MemoryInfoGet(void *po)
pthread_mutex_lock(&lock); pthread_mutex_lock(&lock);
p = ((MEM_BOUND_TYPE *) po) - 1;
p = ((MemoryInfo *) p) - 1; p = ((MemoryInfo *) p) - 1;
if (p < heapStart || p > heapEnd)
{
pthread_mutex_unlock(&lock);
return NULL;
}
if (((MemoryInfo *)p)->magic != MEM_MAGIC) if (((MemoryInfo *)p)->magic != MEM_MAGIC)
{ {
@ -364,7 +399,7 @@ MemoryInfoGetSize(MemoryInfo * a)
return 0; return 0;
} }
return a->size ? a->size - (2 * sizeof(MEM_BOUND_TYPE)) : 0; return a->size ? a->unalignedSize : 0;
} }
const char * const char *
@ -397,7 +432,7 @@ MemoryInfoGetPointer(MemoryInfo * a)
return NULL; return NULL;
} }
return ((MEM_BOUND_TYPE *) a->pointer) + 1; return a->pointer;
} }
void void

View file

@ -29,7 +29,6 @@
#include <limits.h> #include <limits.h>
/* TODO: Verify LibreSSL support later */ /* TODO: Verify LibreSSL support later */
#include <Tls.h>
#if defined(TLS_IMPL) && (TLS_IMPL == TLS_OPENSSL) #if defined(TLS_IMPL) && (TLS_IMPL == TLS_OPENSSL)
#include <openssl/sha.h> #include <openssl/sha.h>

View file

@ -31,7 +31,6 @@
/* TODO: Verify LibreSSL support later */ /* TODO: Verify LibreSSL support later */
#include <Tls.h>
#if defined(TLS_IMPL) && (TLS_IMPL == TLS_OPENSSL) #if defined(TLS_IMPL) && (TLS_IMPL == TLS_OPENSSL)
#include <openssl/sha.h> #include <openssl/sha.h>

View file

@ -1,426 +0,0 @@
/*
* Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> with
* other valuable contributors. See CONTRIBUTORS.txt for the full list.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <Tls.h>
#if TLS_IMPL == TLS_MBEDTLS
#include "mbedtls/net_sockets.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
#include "mbedtls/error.h"
#include <Memory.h>
#include <Log.h>
#include <Str.h>
#include <string.h>
#include <stdlib.h>
/*
* #include statements and any implementation structures
* needed should go here.
*/
typedef struct MbedCookie {
int fd;
bool serverside;
mbedtls_net_context serverFD;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctrDrbg;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_x509_crt cert;
mbedtls_pk_context serverkey;
} MbedCookie;
static bool
AddPEM(mbedtls_x509_crt *certs, char *path)
{
size_t len;
if (!certs || !path)
{
return false;
}
len = strlen(path);
if (len >= 4 && StrEquals(&path[len - 1 - 4], ".pem"))
{
/* Parse it as a file */
if (mbedtls_x509_crt_parse_file(certs, path) == 0)
{
return true;
}
}
/* Parse it as a directory if it is not a .PEM
* Note that this is non-recursive. */
return mbedtls_x509_crt_parse_path(certs, path) == 0;
}
static bool
RegisterPEMs(mbedtls_x509_crt *certs)
{
char *cafile;
int loaded = 0;
if (!certs)
{
return false;
}
/* Step 0: Load from CYTO_TLS_CA if present to overwrite */
cafile = getenv("CYTO_TLS_CA");
if (AddPEM(certs, cafile))
{
loaded++;
}
/* Step 1: Try /etc/ssl/certs */
if (AddPEM(certs, "/etc/ssl/certs"))
{
loaded++;
}
/* Step 2: Try loading off Mozilla's certificates */
if (AddPEM(certs, "/usr/share/ca-certificates/mozilla"))
{
loaded++;
}
/* Step 3: Try loading from its root directly */
if (AddPEM(certs, "/usr/share/ca-certificates"))
{
loaded++;
}
return loaded != 0;
}
void *
TlsInitClient(int fd, const char *serverName)
{
MbedCookie *cookie;
int err;
if (!serverName)
{
return NULL;
}
cookie = Malloc(sizeof(MbedCookie));
memset(cookie, 0, sizeof(MbedCookie));
cookie->fd = fd;
cookie->serverside = false;
/* Initialise MbedTLS */
mbedtls_net_init(&cookie->serverFD);
mbedtls_ssl_init(&cookie->ssl);
mbedtls_ssl_config_init(&cookie->conf);
mbedtls_x509_crt_init(&cookie->cert);
mbedtls_ctr_drbg_init(&cookie->ctrDrbg);
mbedtls_pk_init(&cookie->serverkey);
mbedtls_entropy_init(&cookie->entropy);
err = mbedtls_ctr_drbg_seed(
&cookie->ctrDrbg,
mbedtls_entropy_func,
&cookie->entropy,
(const unsigned char *) serverName, strlen(serverName)
);
if (err != 0)
{
Log(LOG_ERR, "MbedTLS failure on client init: %d", err);
goto error;
}
cookie->serverFD.fd = fd;
err = mbedtls_ssl_config_defaults(
&cookie->conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT
);
if (err != 0)
{
char message[256];
mbedtls_strerror(err, message, 255);
Log(LOG_ERR, "MbedTLS failure on client config: %s", message);
goto error;
}
/* Setup key verification */
if (!RegisterPEMs(&cookie->cert))
{
char message[256];
mbedtls_strerror(err, message, 255);
Log(LOG_ERR, "MbedTLS failure on client certs: %s", message);
goto error;
}
mbedtls_ssl_conf_authmode(&cookie->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&cookie->conf, &cookie->cert, NULL);
/* Setup some callbacks */
mbedtls_ssl_conf_rng(
&cookie->conf,
mbedtls_ctr_drbg_random,
&cookie->ctrDrbg
);
if ((err = mbedtls_ssl_setup(&cookie->ssl, &cookie->conf)) != 0)
{
char message[256];
mbedtls_strerror(err, message, 255);
Log(LOG_ERR, "MbedTLS failure on SSL setup: %s", message);
goto error;
}
/* Setup some functions */
mbedtls_ssl_set_bio(
&cookie->ssl, &cookie->serverFD,
mbedtls_net_send, mbedtls_net_recv, NULL
);
/* Setup the servername */
if ((err = mbedtls_ssl_set_hostname(&cookie->ssl, serverName)) != 0)
{
char message[256];
mbedtls_strerror(err, message, 255);
Log(LOG_ERR, "MbedTLS failure on client hostname: %s", message);
goto error;
}
while ((err = mbedtls_ssl_handshake(&cookie->ssl)) != 0)
{
char message[256];
switch (err)
{
case MBEDTLS_ERR_SSL_WANT_WRITE:
case MBEDTLS_ERR_SSL_WANT_READ:
break;
default:
mbedtls_strerror(err, message, 255);
Log(LOG_ERR, "MbedTLS failure on handshake: %s", message);
goto error;
}
}
return cookie;
error:
mbedtls_net_free(&cookie->serverFD);
mbedtls_ssl_free(&cookie->ssl);
mbedtls_ssl_config_free(&cookie->conf);
mbedtls_ctr_drbg_free(&cookie->ctrDrbg);
mbedtls_entropy_free(&cookie->entropy);
Free(cookie);
return NULL;
}
void *
TlsInitServer(int fd, const char *crt, const char *key)
{
MbedCookie *cookie;
char *seed;
int err;
if (!crt || !key)
{
return NULL;
}
cookie = Malloc(sizeof(MbedCookie));
memset(cookie, 0, sizeof(MbedCookie));
cookie->fd = fd;
cookie->serverside = true;
/* Initialise MbedTLS */
mbedtls_net_init(&cookie->serverFD);
mbedtls_ssl_init(&cookie->ssl);
mbedtls_ssl_config_init(&cookie->conf);
mbedtls_x509_crt_init(&cookie->cert);
mbedtls_ctr_drbg_init(&cookie->ctrDrbg);
mbedtls_pk_init(&cookie->serverkey);
mbedtls_entropy_init(&cookie->entropy);
err = mbedtls_ctr_drbg_seed(
&cookie->ctrDrbg,
mbedtls_entropy_func,
&cookie->entropy,
(const unsigned char *) key, strlen(key)
);
if (err != 0)
{
Log(LOG_ERR, "MbedTLS failure on server init: %d", err);
goto error;
}
/* Add a source of entropy if possible(using the CYTO_TLS_SEED env).
* Note that we ignore the error code. */
seed = getenv("CYTO_TLS_SEED");
mbedtls_entropy_update_seed_file(&cookie->entropy, seed);
/* Setup key verification */
if ((err = mbedtls_x509_crt_parse_file(&cookie->cert, crt)) != 0)
{
char message[256];
mbedtls_strerror(err, message, 255);
Log(LOG_ERR, "MbedTLS failure on server certs: %s", message);
goto error;
}
if ((err = mbedtls_pk_parse_keyfile(&cookie->serverkey, key, NULL, mbedtls_entropy_func, &cookie->ctrDrbg)) != 0)
{
char message[256];
mbedtls_strerror(err, message, 255);
Log(LOG_ERR, "MbedTLS failure on server certs: %s", message);
goto error;
}
mbedtls_ssl_conf_ca_chain(&cookie->conf, cookie->cert.next, NULL);
if ((err = mbedtls_ssl_conf_own_cert(&cookie->conf, &cookie->cert, &cookie->serverkey)) != 0)
{
char message[256];
mbedtls_strerror(err, message, 255);
Log(LOG_ERR, "MbedTLS failure on server certs: %s", message);
goto error;
}
/* Setup SSL */
cookie->serverFD.fd = fd;
err = mbedtls_ssl_config_defaults(
&cookie->conf,
MBEDTLS_SSL_IS_SERVER,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT
);
if (err != 0)
{
char message[256];
mbedtls_strerror(err, message, 255);
Log(LOG_ERR, "MbedTLS failure on server SSL: %s", message);
goto error;
}
/* Setup some callbacks */
mbedtls_ssl_conf_rng(
&cookie->conf,
mbedtls_ctr_drbg_random,
&cookie->ctrDrbg
);
mbedtls_ssl_conf_ca_chain(&cookie->conf, cookie->cert.next, NULL);
if ((err = mbedtls_ssl_setup(&cookie->ssl, &cookie->conf)) != 0)
{
char message[256];
mbedtls_strerror(err, message, 255);
Log(LOG_ERR, "MbedTLS failure on SSL setup: %s", message);
goto error;
}
/* Client connexion */
/* Setup some functions */
mbedtls_ssl_set_bio(
&cookie->ssl, &cookie->serverFD,
mbedtls_net_send, mbedtls_net_recv, NULL
);
/* Handshake the client */
while ((err = mbedtls_ssl_handshake(&cookie->ssl)) != 0)
{
switch (err)
{
case MBEDTLS_ERR_SSL_WANT_WRITE:
case MBEDTLS_ERR_SSL_WANT_READ:
break;
default:
goto error;
}
}
return cookie;
error:
mbedtls_net_free(&cookie->serverFD);
mbedtls_ssl_free(&cookie->ssl);
mbedtls_ssl_config_free(&cookie->conf);
mbedtls_ctr_drbg_free(&cookie->ctrDrbg);
mbedtls_entropy_free(&cookie->entropy);
Free(cookie);
return NULL;
}
ssize_t
TlsRead(void *cookie, void *buf, size_t nBytes)
{
MbedCookie *cooked = cookie;
int ret;
if (!cookie || !buf)
{
return -1;
}
ret = mbedtls_ssl_read(&cooked->ssl, buf, nBytes);
if (ret <= 0)
{
ret = -1;
}
return ret;
}
ssize_t
TlsWrite(void *cookie, void *buf, size_t nBytes)
{
MbedCookie *cooked = cookie;
int ret;
if (!cookie || !buf)
{
return -1;
}
ret = mbedtls_ssl_write(&cooked->ssl, buf, nBytes);
if (ret <= 0)
{
ret = -1;
}
return ret;
}
int
TlsClose(void *cookie)
{
MbedCookie *cooked = cookie;
if (!cookie)
{
return -1;
}
mbedtls_net_free(&cooked->serverFD);
mbedtls_ssl_free(&cooked->ssl);
mbedtls_ssl_config_free(&cooked->conf);
mbedtls_ctr_drbg_free(&cooked->ctrDrbg);
mbedtls_entropy_free(&cooked->entropy);
mbedtls_x509_crt_free(&cooked->cert);
if (cooked->serverside)
{
mbedtls_pk_free(&cooked->serverkey);
}
Free(cooked);
return 0;
}
#endif

View file

@ -399,7 +399,7 @@ end_loop:
} }
else if (StrEquals(op, "def?")) else if (StrEquals(op, "def?"))
{ {
Definition *def = NULL; Definition *def;
size_t i; size_t i;
char *directive = Eval(&argv, stack); char *directive = Eval(&argv, stack);