mirror of
https://github.com/falk-werner/webfused
synced 2024-10-27 20:44:08 +00:00
imported sources from webfuse
This commit is contained in:
parent
da6b538fbb
commit
458363f103
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/build/
|
||||
/.deps/
|
85
.travis.yml
Normal file
85
.travis.yml
Normal file
@ -0,0 +1,85 @@
|
||||
dist: bionic
|
||||
|
||||
language: c
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
packages:
|
||||
- build-essential
|
||||
- cmake
|
||||
- pkgconf
|
||||
- wget
|
||||
- ca-certificates
|
||||
- openssl
|
||||
- libssl-dev
|
||||
- uuid-dev
|
||||
- udev
|
||||
- gettext
|
||||
- python3
|
||||
- python3-pip
|
||||
- python3-setuptools
|
||||
- python3-wheel
|
||||
- ninja
|
||||
|
||||
|
||||
before_install:
|
||||
- mkdir .deps
|
||||
- cd .deps
|
||||
# libfuse
|
||||
- pip3 install --system meson
|
||||
- wget https://github.com/libfuse/libfuse/archive/fuse-3.9.0.tar.gz -O fuse.tar.gz
|
||||
- tar -xf fuse.tar.gz
|
||||
- cd libfuse-fuse-3.9.0
|
||||
- mkdir .build
|
||||
- cd .build
|
||||
- meson ..
|
||||
- ninja
|
||||
- ninja install
|
||||
- cd ..
|
||||
- cd ..
|
||||
# libwebsockets
|
||||
- wget https://github.com/warmcat/libwebsockets/archive/v3.2.0.tar.gz -O libwebsockets.tar.gz
|
||||
- tar -xf libwebsockets.tar.gz
|
||||
- cd libwebsockets-3.2.0
|
||||
- mkdir .build
|
||||
- cd .build
|
||||
- cmake ..
|
||||
- make
|
||||
- make install
|
||||
- cd ..
|
||||
- cd ..
|
||||
# jansson
|
||||
- wget https://github.com/akheron/jansson/archive/v2.12.tar.gz -O jansson.tar.gz
|
||||
- tar -xf jansson.tar.gz
|
||||
- cd jansson-2.12
|
||||
- mkdir .build
|
||||
- cd .build
|
||||
- cmake ..
|
||||
- make
|
||||
- make install
|
||||
- cd ..
|
||||
- cd ..
|
||||
# libwebfuse
|
||||
- wget https://github.com/falk-werner/webfuse/archive/master.zip -O webfuse.tar.gz
|
||||
- tar -xf webfuse.tar.gz
|
||||
- cd webfuse-master
|
||||
- mkdir .build
|
||||
- cd .build
|
||||
- cmake ..
|
||||
- make
|
||||
- make install
|
||||
- cd ..
|
||||
- cd ..
|
||||
- cd ..
|
||||
|
||||
before_script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake ..
|
||||
|
||||
script:
|
||||
make
|
75
CMakeLists.txt
Normal file
75
CMakeLists.txt
Normal file
@ -0,0 +1,75 @@
|
||||
cmake_minimum_required (VERSION 3.10)
|
||||
project(webfused VERSION 0.1.0 DESCRIPTION "Webfuse daemon")
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(FUSE3 REQUIRED fuse3)
|
||||
pkg_check_modules(LWS REQUIRED libwebsockets)
|
||||
pkg_check_modules(JANSSON REQUIRED jansson)
|
||||
pkg_check_modules(UUID REQUIRED uuid)
|
||||
pkg_check_modules(OPENSSL REQUIRED openssl)
|
||||
pkg_check_modules(WEBFUSE REQUIRED libwebfuse-adapter)
|
||||
|
||||
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||
|
||||
include_directories(
|
||||
"src"
|
||||
${WEBFUSE_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
link_directories(
|
||||
${WEBFUSE_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(C_WARNINGS -Wall -Wextra)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
add_library(userdb STATIC
|
||||
src/userdb/userdb.c
|
||||
)
|
||||
|
||||
target_include_directories(userdb PUBLIC
|
||||
${OPENSSL_INCLUDE_DIRS}
|
||||
${JANSSON_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_compile_options(userdb PUBLIC ${OPENSSL_CFLAGS_OTHER})
|
||||
|
||||
|
||||
add_executable(webfused
|
||||
src/daemon/main.c
|
||||
)
|
||||
|
||||
target_link_libraries(webfused PUBLIC
|
||||
userdb
|
||||
${OPENSSL_LIBRARIES}
|
||||
${WEBFUSE_LIBRARIES}
|
||||
${UUID_LIBRARIES}
|
||||
)
|
||||
|
||||
target_compile_options(webfused PUBLIC ${OPENSSL_CFLAGS_OTHER})
|
||||
|
||||
add_executable(webfuse-passwd
|
||||
src/passwd/main.c
|
||||
)
|
||||
|
||||
target_link_libraries(webfuse-passwd PUBLIC
|
||||
userdb
|
||||
${OPENSSL_LIBRARIES}
|
||||
${JANSSON_LIBRARIES}
|
||||
)
|
||||
|
||||
target_include_directories(webfuse-passwd PUBLIC
|
||||
example/passwd
|
||||
example/lib/userdb/include
|
||||
${OPENSSL_INCLUDE_DIRS}
|
||||
${JANSSON_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_compile_options(webfuse-passwd PUBLIC ${OPENSSL_CFLAGS_OTHER})
|
196
src/daemon/main.c
Normal file
196
src/daemon/main.c
Normal file
@ -0,0 +1,196 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <webfuse_adapter.h>
|
||||
#include <userdb/userdb.h>
|
||||
|
||||
#define SERVICE_TIMEOUT (1 * 1000)
|
||||
|
||||
struct args
|
||||
{
|
||||
struct wf_server_config * config;
|
||||
char * passwd_path;
|
||||
bool show_help;
|
||||
};
|
||||
|
||||
static bool shutdown_requested = false;
|
||||
|
||||
static void show_help(void)
|
||||
{
|
||||
printf(
|
||||
"webfused, Copyright (c) 2019, webfused authors <https://github.com/falk-werner/webfused>\n"
|
||||
"Websocket file system daemon\n"
|
||||
"\n"
|
||||
"Usage: webfused [m <mount_point>] [-d <document_root] [-n <vhost_name>] [-p <port>]\n"
|
||||
" [-c <server_cert_path>] [-k <server_key_path>] [-P <passwd_path>]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
"\t-m, --mount_point Path of mount point (required)\n"
|
||||
"\t-d, --document_root Path of www directory (default: not set, www disabled)\n"
|
||||
"\t-c, --server_cert_path Path of servers own certificate (default: not set, TLS disabled)\n"
|
||||
"\t-k, --server_key_path Path of servers private key (default: not set, TLS disabled)\n"
|
||||
"\t-n, --vhost_name Name of virtual host (default: \"localhost\")\n"
|
||||
"\t-p, --port Number of servers port (default: 8080)\n"
|
||||
"\t-P, --passwd_path Path to password file (default: not set, authentication disabled)\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
static bool authenticate(struct wf_credentials * creds, void * user_data)
|
||||
{
|
||||
bool result = false;
|
||||
struct args * args = user_data;
|
||||
|
||||
char const * username = wf_credentials_get(creds, "username");
|
||||
char const * password = wf_credentials_get(creds, "password");
|
||||
if ((NULL != username) && (NULL != password))
|
||||
{
|
||||
struct userdb * db = userdb_create("");
|
||||
result = userdb_load(db, args->passwd_path);
|
||||
if (result)
|
||||
{
|
||||
result = userdb_check(db, username, password);
|
||||
}
|
||||
|
||||
userdb_dispose(db);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int parse_arguments(int argc, char * argv[], struct args * args)
|
||||
{
|
||||
static struct option const options[] =
|
||||
{
|
||||
{"mount_point", required_argument, NULL, 'm'},
|
||||
{"document_root", required_argument, NULL, 'd'},
|
||||
{"server_cert_path", required_argument, NULL, 'c'},
|
||||
{"server_key_path", required_argument, NULL, 'k'},
|
||||
{"vhost_name", required_argument, NULL, 'n'},
|
||||
{"port", required_argument, NULL, 'p'},
|
||||
{"passwd_path", required_argument, NULL, 'P'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
bool result = EXIT_SUCCESS;
|
||||
bool finished = false;
|
||||
bool has_mountpoint = false;
|
||||
while ((!finished) && (EXIT_SUCCESS == result))
|
||||
{
|
||||
int option_index = 0;
|
||||
int const c = getopt_long(argc, argv, "m:d:c:k:n:p:P:h", options, &option_index);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case -1:
|
||||
finished = true;
|
||||
break;
|
||||
case 'h':
|
||||
args->show_help = true;
|
||||
finished = true;
|
||||
break;
|
||||
case 'm':
|
||||
wf_server_config_set_mountpoint(args->config, optarg);
|
||||
has_mountpoint = true;
|
||||
break;
|
||||
case 'd':
|
||||
wf_server_config_set_documentroot(args->config, optarg);
|
||||
break;
|
||||
case 'c':
|
||||
wf_server_config_set_certpath(args->config, optarg);
|
||||
break;
|
||||
case 'k':
|
||||
wf_server_config_set_keypath(args->config, optarg);
|
||||
break;
|
||||
case 'n':
|
||||
wf_server_config_set_vhostname(args->config, optarg);
|
||||
break;
|
||||
case 'p':
|
||||
wf_server_config_set_port(args->config, atoi(optarg));
|
||||
break;
|
||||
case 'P':
|
||||
free(args->passwd_path);
|
||||
args->passwd_path = strdup(optarg);
|
||||
wf_server_config_add_authenticator(args->config,
|
||||
"username",
|
||||
&authenticate,
|
||||
args);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "error: unknown argument\n");
|
||||
result = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((EXIT_SUCCESS == result) && (!args->show_help))
|
||||
{
|
||||
if (!has_mountpoint)
|
||||
{
|
||||
fprintf(stderr, "error: missing mount point\n");
|
||||
result = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (EXIT_SUCCESS != result)
|
||||
{
|
||||
args->show_help = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void on_interrupt(int signal_id)
|
||||
{
|
||||
(void) signal_id;
|
||||
|
||||
shutdown_requested = true;
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
struct args args;
|
||||
args.config = wf_server_config_create();
|
||||
wf_server_config_set_vhostname(args.config, "localhost");
|
||||
wf_server_config_set_port(args.config, 8080);
|
||||
args.passwd_path = NULL;
|
||||
args.show_help = false;
|
||||
|
||||
int result = parse_arguments(argc, argv, &args);
|
||||
|
||||
if (!args.show_help)
|
||||
{
|
||||
signal(SIGINT, on_interrupt);
|
||||
struct wf_server * server = wf_server_create(args.config);
|
||||
if (NULL != server)
|
||||
{
|
||||
while (!shutdown_requested)
|
||||
{
|
||||
wf_server_service(server, SERVICE_TIMEOUT);
|
||||
}
|
||||
|
||||
wf_server_dispose(server);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "fatal: unable start server\n");
|
||||
result = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
show_help();
|
||||
}
|
||||
|
||||
free(args.passwd_path);
|
||||
wf_server_config_dispose(args.config);
|
||||
return result;
|
||||
}
|
||||
|
304
src/passwd/main.c
Normal file
304
src/passwd/main.c
Normal file
@ -0,0 +1,304 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/opensslv.h>
|
||||
#include <openssl/engine.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <jansson.h>
|
||||
#include <userdb/userdb.h>
|
||||
|
||||
|
||||
struct args
|
||||
{
|
||||
char * file;
|
||||
char * command;
|
||||
char * username;
|
||||
char * password;
|
||||
char * pepper;
|
||||
bool show_help;
|
||||
};
|
||||
|
||||
typedef int command_invoke_fn(
|
||||
struct args * args);
|
||||
|
||||
struct command
|
||||
{
|
||||
char const * name;
|
||||
command_invoke_fn * invoke;
|
||||
};
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
printf(
|
||||
"webfuse-passwd, Copyright (c) 2019, webfused authors <https://github.com/falk-werner/webfused>\n"
|
||||
"Manage webfuse passwd file\n"
|
||||
"\n"
|
||||
"Usage: webfuse-passwd -f <file> -c <command> [-u <username>] [-p <password>] [-P <pepper>]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
"\t-f, --file Path of wf passwd file\n"
|
||||
"\t-c, --command Command to execute\n"
|
||||
"\t-u, --username Name of user\n"
|
||||
"\t-p, --password Password of user\n"
|
||||
"\t-P, --pepper pepper\n"
|
||||
"\t-h, --help Shows this message\n"
|
||||
"\n"
|
||||
"Commands:\n"
|
||||
"\tcreate Creates an empty passwd file (or cleans an existing)\n"
|
||||
"\t Example: webfuse-passwd -f passwd.json -c create\n"
|
||||
"\tadd Adds or replaces a user\n"
|
||||
"\t Example: webfuse-passwd -f passwd.json -c add -u bob -p secret\n"
|
||||
"\tremove Removes a user\n"
|
||||
"\t Example: webfuse-passwd -f passwd.json -c remove -u bob\n"
|
||||
"\tcheck Checks password of a user\n"
|
||||
"\t Example: webfuse-passwd -f passwd.json -c check -u bob -p secret\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static int parse_args(struct args * args, int argc, char * argv[])
|
||||
{
|
||||
static struct option const options[] =
|
||||
{
|
||||
{"file", required_argument, NULL, 'f'},
|
||||
{"command", required_argument, NULL, 'c'},
|
||||
{"username", required_argument, NULL, 'u'},
|
||||
{"password", required_argument, NULL, 'p'},
|
||||
{"Pepper", required_argument, NULL, 'P'},
|
||||
{"help", required_argument, NULL, 'h'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
int result = EXIT_SUCCESS;
|
||||
bool finished = false;
|
||||
while ((!finished) && (EXIT_SUCCESS == result))
|
||||
{
|
||||
int option_index = 0;
|
||||
int const c = getopt_long(argc, argv, "f:c:u:p:P:h", options, &option_index);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case -1:
|
||||
finished = true;
|
||||
break;
|
||||
case 'h':
|
||||
args->show_help = true;
|
||||
finished = true;
|
||||
break;
|
||||
case 'f':
|
||||
free(args->file);
|
||||
args->file = strdup(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
free(args->command);
|
||||
args->command = strdup(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
free(args->username);
|
||||
args->username = strdup(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
free(args->password);
|
||||
args->password = strdup(optarg);
|
||||
break;
|
||||
case 'P':
|
||||
free(args->pepper);
|
||||
args->pepper = strdup(optarg);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "error: unknown argument\n");
|
||||
result = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((result == EXIT_SUCCESS) && (!args->show_help))
|
||||
{
|
||||
if (NULL == args->file)
|
||||
{
|
||||
fprintf(stderr, "error: missing file\n");
|
||||
args->show_help = true;
|
||||
result = EXIT_FAILURE;
|
||||
}
|
||||
else if (NULL == args->command)
|
||||
{
|
||||
fprintf(stderr, "error: missing command\n");
|
||||
args->show_help = true;
|
||||
result = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void args_init(struct args * args)
|
||||
{
|
||||
args->file = NULL;
|
||||
args->command = NULL;
|
||||
args->username = NULL;
|
||||
args->password = NULL;
|
||||
args->pepper = strdup("");
|
||||
args->show_help = false;
|
||||
}
|
||||
|
||||
static int create_passwd(struct args * args)
|
||||
{
|
||||
struct userdb * db = userdb_create(args->pepper);
|
||||
bool result = userdb_save(db, args->file);
|
||||
userdb_dispose(db);
|
||||
|
||||
return (result) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int add_user(struct args * args)
|
||||
{
|
||||
if (NULL == args->username)
|
||||
{
|
||||
fprintf(stderr, "error: missing username");
|
||||
args->show_help = true;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (NULL == args->password)
|
||||
{
|
||||
fprintf(stderr, "error: missing password");
|
||||
args->show_help = true;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
struct userdb * db = userdb_create(args->pepper);
|
||||
userdb_load(db, args->file);
|
||||
userdb_add(db, args->username, args->password);
|
||||
bool result = userdb_save(db, args->file);
|
||||
userdb_dispose(db);
|
||||
|
||||
return (result) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int remove_user(struct args * args)
|
||||
{
|
||||
if (NULL == args->username)
|
||||
{
|
||||
fprintf(stderr, "error: missing username");
|
||||
args->show_help = true;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
struct userdb * db = userdb_create(args->pepper);
|
||||
userdb_load(db, args->file);
|
||||
userdb_remove(db, args->username);
|
||||
bool result = userdb_save(db, args->file);
|
||||
userdb_dispose(db);
|
||||
|
||||
return (result) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int check_password(struct args * args)
|
||||
{
|
||||
if (NULL == args->username)
|
||||
{
|
||||
fprintf(stderr, "error: missing username");
|
||||
args->show_help = true;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (NULL == args->password)
|
||||
{
|
||||
fprintf(stderr, "error: missing password");
|
||||
args->show_help = true;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
struct userdb * db = userdb_create(args->pepper);
|
||||
userdb_load(db, args->file);
|
||||
bool result = userdb_check(db, args->username, args->password);
|
||||
userdb_dispose(db);
|
||||
|
||||
printf("%s\n", (result) ? "OK" : "FAILURE");
|
||||
return (result) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int invoke_invalid_command(struct args * args)
|
||||
{
|
||||
(void) args;
|
||||
|
||||
fprintf(stderr, "error: unknown command\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static struct command const commands[] =
|
||||
{
|
||||
{"create", &create_passwd},
|
||||
{"add", &add_user},
|
||||
{"remove", &remove_user},
|
||||
{"check", &check_password},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static struct command const invalid_command =
|
||||
{
|
||||
"<invalid>",
|
||||
&invoke_invalid_command
|
||||
};
|
||||
|
||||
static struct command const * get_command(char const * name)
|
||||
{
|
||||
for(size_t i = 0; NULL != commands[i].name; i++)
|
||||
{
|
||||
if (0 == strcmp(name, commands[i].name))
|
||||
{
|
||||
return &commands[i];
|
||||
}
|
||||
}
|
||||
|
||||
return &invalid_command;
|
||||
}
|
||||
|
||||
static void args_cleanup(struct args * args)
|
||||
{
|
||||
free(args->file);
|
||||
free(args->command);
|
||||
free(args->username);
|
||||
free(args->password);
|
||||
free(args->pepper);
|
||||
}
|
||||
|
||||
static void openssl_cleanup(void)
|
||||
{
|
||||
FIPS_mode_set(0);
|
||||
ENGINE_cleanup();
|
||||
CONF_modules_unload(1);
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
ERR_free_strings();
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
OPENSSL_init();
|
||||
OPENSSL_add_all_algorithms_conf();
|
||||
|
||||
struct args args;
|
||||
args_init(&args);
|
||||
int result = parse_args(&args, argc, argv);
|
||||
if ((EXIT_SUCCESS == result) && (!args.show_help))
|
||||
{
|
||||
struct command const * command = get_command(args.command);
|
||||
result = command->invoke(&args);
|
||||
}
|
||||
|
||||
if (args.show_help)
|
||||
{
|
||||
print_usage();
|
||||
}
|
||||
|
||||
args_cleanup(&args);
|
||||
openssl_cleanup();
|
||||
return result;
|
||||
}
|
277
src/userdb/userdb.c
Normal file
277
src/userdb/userdb.c
Normal file
@ -0,0 +1,277 @@
|
||||
#include "userdb.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <jansson.h>
|
||||
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#define USERDB_HASH_ALGORITHM "sha512"
|
||||
#define USERDB_MAJOR 1
|
||||
#define USERDB_MINOR 0
|
||||
|
||||
#define USERDB_SALT_SIZE 32
|
||||
|
||||
struct userdb
|
||||
{
|
||||
json_t * users;
|
||||
char * pepper;
|
||||
char * hash_algorithm;
|
||||
};
|
||||
|
||||
static bool is_compatible(json_t * meta)
|
||||
{
|
||||
bool result = false;
|
||||
if (json_is_object(meta))
|
||||
{
|
||||
json_t * type = json_object_get(meta, "type");
|
||||
json_t * major = json_object_get(meta, "major");
|
||||
json_t * minor = json_object_get(meta, "minor");
|
||||
json_t * hash_algorithm = json_object_get(meta, "hash_algorithm");
|
||||
|
||||
result = (
|
||||
json_is_string(type) &&
|
||||
(0 == strcmp(json_string_value(type), "wf-userdb")) &&
|
||||
json_is_integer(major) &&
|
||||
(USERDB_MAJOR == json_integer_value(major)) &&
|
||||
json_is_integer(minor) &&
|
||||
json_is_string(hash_algorithm)
|
||||
);
|
||||
|
||||
if (result)
|
||||
{
|
||||
char const * algorithm = json_string_value(hash_algorithm);
|
||||
result = (NULL != EVP_get_digestbyname(algorithm));
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static char hex_char(unsigned char value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 0x00: return '0';
|
||||
case 0x01: return '1';
|
||||
case 0x02: return '2';
|
||||
case 0x03: return '3';
|
||||
case 0x04: return '4';
|
||||
case 0x05: return '5';
|
||||
case 0x06: return '6';
|
||||
case 0x07: return '7';
|
||||
case 0x08: return '8';
|
||||
case 0x09: return '9';
|
||||
case 0x0a: return 'a';
|
||||
case 0x0b: return 'b';
|
||||
case 0x0c: return 'c';
|
||||
case 0x0d: return 'd';
|
||||
case 0x0e: return 'e';
|
||||
case 0x0f: return 'f';
|
||||
default: return '?';
|
||||
}
|
||||
}
|
||||
|
||||
static char * to_hex(unsigned char const * value, size_t length)
|
||||
{
|
||||
char * result = malloc((2 * length) + 1);
|
||||
if (NULL != result)
|
||||
{
|
||||
for (size_t i = 0, j = 0; i < length; i++, j+=2)
|
||||
{
|
||||
unsigned char high = (value[i] >> 4) & 0x0f;
|
||||
unsigned char low = value[i] & 0x0f;
|
||||
|
||||
result[j ] = hex_char(high);
|
||||
result[j + 1] = hex_char(low);
|
||||
}
|
||||
|
||||
result[2 * length] = '\0';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static char * generate_salt(void)
|
||||
{
|
||||
unsigned char buffer[USERDB_SALT_SIZE];
|
||||
int rc = RAND_bytes(buffer, USERDB_SALT_SIZE);
|
||||
if (1 != rc)
|
||||
{
|
||||
fprintf(stderr, "fatal: failed to generate salt (OpenSSL RAND_bytes failed)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return to_hex(buffer, USERDB_SALT_SIZE);
|
||||
}
|
||||
|
||||
static char * compute_hash(
|
||||
struct userdb * db,
|
||||
char const * password,
|
||||
char const * salt)
|
||||
{
|
||||
EVP_MD const * digest = EVP_get_digestbyname(db->hash_algorithm);
|
||||
if (NULL == digest)
|
||||
{
|
||||
fprintf(stderr, "error: hash algorithm %s not supported\n", db->hash_algorithm);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char * result = NULL;
|
||||
unsigned int hash_size = EVP_MD_size(digest);
|
||||
unsigned char * hash = malloc(hash_size);
|
||||
|
||||
if (NULL != hash)
|
||||
{
|
||||
EVP_MD_CTX * context = EVP_MD_CTX_new();
|
||||
EVP_DigestInit_ex(context, digest, NULL);
|
||||
EVP_DigestUpdate(context, password, strlen(password));
|
||||
EVP_DigestUpdate(context, salt, strlen(salt));
|
||||
EVP_DigestUpdate(context, db->pepper, strlen(db->pepper));
|
||||
EVP_DigestFinal_ex(context, hash, &hash_size);
|
||||
EVP_MD_CTX_free(context);
|
||||
|
||||
result = to_hex(hash, hash_size);
|
||||
free(hash);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct userdb * userdb_create(
|
||||
char const * pepper)
|
||||
{
|
||||
struct userdb * db = malloc(sizeof(struct userdb));
|
||||
if (NULL != db)
|
||||
{
|
||||
db->users = json_object();
|
||||
db->pepper = strdup(pepper);
|
||||
db->hash_algorithm = strdup(USERDB_HASH_ALGORITHM);
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
void userdb_dispose(
|
||||
struct userdb * db)
|
||||
{
|
||||
json_decref(db->users);
|
||||
free(db->pepper);
|
||||
free(db->hash_algorithm);
|
||||
free(db);
|
||||
}
|
||||
|
||||
bool userdb_save(
|
||||
struct userdb * db,
|
||||
char const * filename)
|
||||
{
|
||||
json_t * container = json_object();
|
||||
|
||||
json_t * meta = json_object();
|
||||
json_object_set_new(meta, "type", json_string("wf-userdb"));
|
||||
json_object_set_new(meta, "major", json_integer(USERDB_MAJOR));
|
||||
json_object_set_new(meta, "minor", json_integer(USERDB_MINOR));
|
||||
json_object_set_new(meta, "hash_algorithm", json_string(db->hash_algorithm));
|
||||
json_object_set_new(container, "meta", meta);
|
||||
|
||||
json_object_set(container, "users", db->users);
|
||||
|
||||
int result = json_dump_file(container, filename, JSON_INDENT(2));
|
||||
json_decref(container);
|
||||
|
||||
return (0 == result);
|
||||
}
|
||||
|
||||
|
||||
bool userdb_load(
|
||||
struct userdb * db,
|
||||
char const * filename)
|
||||
{
|
||||
bool result = false;
|
||||
json_t * container = json_load_file(filename, 0, NULL);
|
||||
if (NULL != container)
|
||||
{
|
||||
json_t * meta = json_object_get(container, "meta");
|
||||
json_t * users = json_object_get(container, "users");
|
||||
|
||||
if ((is_compatible(meta)) && (json_is_object(users))) {
|
||||
json_t * hash_algorithm = json_object_get(meta, "hash_algorithm");
|
||||
free(db->hash_algorithm);
|
||||
db->hash_algorithm = strdup(json_string_value(hash_algorithm));
|
||||
|
||||
json_decref(db->users);
|
||||
json_incref(users);
|
||||
db->users = users;
|
||||
|
||||
result = true;
|
||||
}
|
||||
|
||||
json_decref(container);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void userdb_add(
|
||||
struct userdb * db,
|
||||
char const * username,
|
||||
char const * password)
|
||||
{
|
||||
char * salt = generate_salt();
|
||||
char * hash = compute_hash(db, password, salt);
|
||||
|
||||
json_t * user = json_object();
|
||||
json_object_set_new(user, "password_hash", json_string(hash));
|
||||
json_object_set_new(user, "salt", json_string(salt));
|
||||
|
||||
json_object_set_new(db->users, username, user);
|
||||
|
||||
free(salt);
|
||||
free(hash);
|
||||
}
|
||||
|
||||
void userdb_remove(
|
||||
struct userdb * db,
|
||||
char const * user)
|
||||
{
|
||||
json_object_del(db->users, user);
|
||||
}
|
||||
|
||||
static char const * json_object_get_string(
|
||||
json_t * object,
|
||||
char const * key)
|
||||
{
|
||||
char const * result = NULL;
|
||||
|
||||
json_t * string_holder = json_object_get(object, key);
|
||||
if (json_is_string(string_holder))
|
||||
{
|
||||
result = json_string_value(string_holder);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool userdb_check(
|
||||
struct userdb * db,
|
||||
char const * username,
|
||||
char const * password)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
json_t * user = json_object_get(db->users, username);
|
||||
if (json_is_object(user))
|
||||
{
|
||||
char const * salt = json_object_get_string(user, "salt");
|
||||
char const * hash = json_object_get_string(user, "password_hash");
|
||||
|
||||
char * computed_hash = compute_hash(db, password, salt);
|
||||
|
||||
result = (0 == strcmp(computed_hash, hash));
|
||||
free(computed_hash);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
46
src/userdb/userdb.h
Normal file
46
src/userdb/userdb.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef USERDB_H
|
||||
#define USERDB_H
|
||||
|
||||
#ifndef __cplusplus
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
struct userdb;
|
||||
|
||||
extern struct userdb * userdb_create(
|
||||
char const * pepper);
|
||||
|
||||
extern void userdb_dispose(struct userdb * db);
|
||||
|
||||
extern bool userdb_save(
|
||||
struct userdb * db,
|
||||
char const * filename);
|
||||
|
||||
extern bool userdb_load(
|
||||
struct userdb * db,
|
||||
char const * filename);
|
||||
|
||||
extern void userdb_add(
|
||||
struct userdb * db,
|
||||
char const * username,
|
||||
char const * password);
|
||||
|
||||
extern void userdb_remove(
|
||||
struct userdb * db,
|
||||
char const * user);
|
||||
|
||||
extern bool userdb_check(
|
||||
struct userdb * db,
|
||||
char const * username,
|
||||
char const * password);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user