From 48185776b6f31b1adb5b9fdad3f7df6a99b7e09f Mon Sep 17 00:00:00 2001 From: Falk Werner <47070255+falk-werner@users.noreply.github.com> Date: Sat, 23 Mar 2019 22:53:14 +0100 Subject: [PATCH] Feature/authentication (#14) * makes wsfs_server_config opaque * feature: try to create mount point, if not present * fixes server start failure due to existing mountpoint * added basic authentication infrastructure * makes wsfs_server_config opaque * feature: try to create mount point, if not present * fixes server start failure due to existing mountpoint * added basic authentication infrastructure * added unit tests for credentials * added unit tests for authenticators * propagates authenticators to server protocol * enabled username authentication in daemon example * adds example to compute password hash * adds infrastructure to execute commands * added userdb to encapsulate authentication stuff * adds session and session_manager * fixes warning about unused param * moves some logic from server_protocol to session * makes wsfs_server_config opaque * feature: try to create mount point, if not present * fixes server start failure due to existing mountpoint * added basic authentication infrastructure * makes wsfs_server_config opaque * added unit tests for credentials * added unit tests for authenticators * propagates authenticators to server protocol * enabled username authentication in daemon example * adds example to compute password hash * adds infrastructure to execute commands * added userdb to encapsulate authentication stuff * adds session and session_manager * fixes warning about unused param * moves some logic from server_protocol to session * updates libcrypto to version 1.1.0 --- CMakeLists.txt | 67 ++++- example/daemon/main.c | 93 ++++--- example/lib/userdb/include/userdb.h | 46 ++++ example/lib/userdb/src/userdb.c | 277 ++++++++++++++++++++ example/passwd/main.c | 304 ++++++++++++++++++++++ include/wsfs/adapter/authenticate.h | 15 ++ include/wsfs/adapter/credentials.h | 25 ++ include/wsfs/adapter/server_config.h | 50 ++-- include/wsfs/adapter/server_protocol.h | 9 +- include/wsfs_adapter.h | 2 + lib/wsfs/adapter/authenticator.c | 49 ++++ lib/wsfs/adapter/authenticator.h | 40 +++ lib/wsfs/adapter/authenticators.c | 101 +++++++ lib/wsfs/adapter/authenticators.h | 51 ++++ lib/wsfs/adapter/credentials.c | 40 +++ lib/wsfs/adapter/credentials_intern.h | 30 +++ lib/wsfs/adapter/server.c | 60 +++-- lib/wsfs/adapter/server_config.c | 84 +++++- lib/wsfs/adapter/server_config_intern.h | 37 +++ lib/wsfs/adapter/server_protocol.c | 73 +++--- lib/wsfs/adapter/server_protocol_intern.h | 8 +- lib/wsfs/adapter/session.c | 84 ++++++ lib/wsfs/adapter/session.h | 64 +++++ lib/wsfs/adapter/session_manager.c | 54 ++++ lib/wsfs/adapter/session_manager.h | 46 ++++ lib/wsfs/provider/operation/getattr.c | 2 +- test/mock_authenticator.cc | 42 +++ test/mock_authenticator.hpp | 34 +++ test/test_authenticator.cc | 63 +++++ test/test_authenticators.cc | 154 +++++++++++ test/test_credentials.cc | 70 +++++ test/test_server.cc | 7 +- 32 files changed, 1969 insertions(+), 112 deletions(-) create mode 100644 example/lib/userdb/include/userdb.h create mode 100644 example/lib/userdb/src/userdb.c create mode 100644 example/passwd/main.c create mode 100644 include/wsfs/adapter/authenticate.h create mode 100644 include/wsfs/adapter/credentials.h create mode 100644 lib/wsfs/adapter/authenticator.c create mode 100644 lib/wsfs/adapter/authenticator.h create mode 100644 lib/wsfs/adapter/authenticators.c create mode 100644 lib/wsfs/adapter/authenticators.h create mode 100644 lib/wsfs/adapter/credentials.c create mode 100644 lib/wsfs/adapter/credentials_intern.h create mode 100644 lib/wsfs/adapter/server_config_intern.h create mode 100644 lib/wsfs/adapter/session.c create mode 100644 lib/wsfs/adapter/session.h create mode 100644 lib/wsfs/adapter/session_manager.c create mode 100644 lib/wsfs/adapter/session_manager.h create mode 100644 test/mock_authenticator.cc create mode 100644 test/mock_authenticator.hpp create mode 100644 test/test_authenticator.cc create mode 100644 test/test_authenticators.cc create mode 100644 test/test_credentials.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b9fc85..8cd01bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,13 @@ install(FILES include/wsfs/status.h DESTINATION include/wsfs) set(WSFS_ADAPTER_SOURCES lib/wsfs/adapter/filesystem.c lib/wsfs/adapter/server.c + lib/wsfs/adapter/server_config.c + lib/wsfs/adapter/server_protocol.c + lib/wsfs/adapter/session.c + lib/wsfs/adapter/session_manager.c + lib/wsfs/adapter/authenticator.c + lib/wsfs/adapter/authenticators.c + lib/wsfs/adapter/credentials.c lib/wsfs/adapter/time/timepoint.c lib/wsfs/adapter/time/timer.c lib/wsfs/adapter/time/timeout_manager.c @@ -67,8 +74,6 @@ set(WSFS_ADAPTER_SOURCES lib/wsfs/adapter/operation/open.c lib/wsfs/adapter/operation/close.c lib/wsfs/adapter/operation/read.c - lib/wsfs/adapter/server_config.c - lib/wsfs/adapter/server_protocol.c lib/wsfs/adapter/jsonrpc/server.c lib/wsfs/adapter/jsonrpc/method.c lib/wsfs/adapter/jsonrpc/request.c @@ -156,6 +161,24 @@ install(FILES "${PROJECT_BINARY_DIR}/libwsfs-provider.pc" DESTINATION lib${LIB_S if(NOT WITHOUT_EXAMPLE) +# libuserdb + +add_library(userdb STATIC + example/lib/userdb/src/userdb.c +) + +target_include_directories(userdb PUBLIC + example/lib/userdb/include + ${OPENSSL_INCLUDE_DIRS} + ${JANSSON_INCLUDE_DIRS} +) + +target_compile_options(userdb PUBLIC + ${C_WARNINGS} + ${OPENSSL_CFLAGS_OTHER} + ${JANSSON_CFLAGS_OTHER} +) + # daemon add_executable(wsfsd @@ -178,6 +201,35 @@ target_link_libraries(wsfs-provider-app PUBLIC wsfs-provider ${EXTRA_LIBS}) target_include_directories(wsfs-provider-app PUBLIC ${EXTRA_INCLUDE_DIRS}) target_compile_options(wsfs-provider-app PUBLIC ${EXTRA_CFLAGS}) +# wsfs-passwd + +pkg_check_modules(OPENSSL REQUIRED openssl) + + +add_executable(wsfs-passwd + example/passwd/main.c +) + +target_link_libraries(wsfs-passwd PUBLIC + userdb + ${OPENSSL_LIBRARIES} + ${JANSSON_LIBRARIES} +) + +target_include_directories(wsfs-passwd PUBLIC + example/passwd + example/lib/userdb/include + ${OPENSSL_INCLUDE_DIRS} + ${JANSSON_INCLUDE_DIRS} +) + +target_compile_options(wsfs-passwd PUBLIC + ${C_WARNINGS} + ${OPENSSL_CFLAGS_OTHER} + ${JANSSON_CFLAGS_OTHER} +) + + endif(NOT WITHOUT_EXAMPLE) # tests @@ -188,6 +240,7 @@ include (CTest) pkg_check_modules(GTEST gtest_main) include(GoogleTest) +pkg_check_modules(GMOCK gmock) add_library(wsfs-adapter-static STATIC ${WSFS_ADAPTER_SOURCES}) set_target_properties(wsfs-adapter-static PROPERTIES OUTPUT_NAME wsfs-adapter) @@ -202,17 +255,21 @@ target_compile_options(wsfs-provider-static PUBLIC ${EXTRA_CFLAGS}) add_executable(alltests test/msleep.cc + test/mock_authenticator.cc test/test_response_parser.cc test/test_server.cc test/test_timepoint.cc test/test_timer.cc test/test_url.cc + test/test_credentials.cc + test/test_authenticator.cc + test/test_authenticators.cc ${WSFS_COMMON_SOURCES} ) -target_link_libraries(alltests PUBLIC wsfs-adapter-static wsfs-provider-static ${EXTRA_LIBS} ${GTEST_LIBRARIES}) -target_include_directories(alltests PUBLIC lib ${EXTRA_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}) -target_compile_options(alltests PUBLIC ${EXTRA_CFLAGS} ${GTEST_CFLAGS}) +target_link_libraries(alltests PUBLIC wsfs-adapter-static wsfs-provider-static ${EXTRA_LIBS} ${GMOCK_LIBRARIES} ${GTEST_LIBRARIES}) +target_include_directories(alltests PUBLIC lib ${EXTRA_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS}) +target_compile_options(alltests PUBLIC ${EXTRA_CFLAGS} ${GMOCK_CFLAGS} ${GTEST_CFLAGS}) enable_testing() gtest_discover_tests(alltests TEST_PREFIX alltests:) diff --git a/example/daemon/main.c b/example/daemon/main.c index 0964674..0614970 100644 --- a/example/daemon/main.c +++ b/example/daemon/main.c @@ -7,12 +7,15 @@ #include #include +#include #include + struct args { - struct wsfs_server_config config; + struct wsfs_server_config * config; + char * passwd_path; bool show_help; }; @@ -25,7 +28,7 @@ static void show_help(void) "Websocket file system daemon\n" "\n" "Usage: wsfsd [m ] [-d ] [-p ]\n" - " [-c -k] []\n" + " [-c ] [-k ] [-P ]\n" "\n" "Options:\n" "\t-m, --mount_point Path of mount point (required)\n" @@ -34,9 +37,39 @@ static void show_help(void) "\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 wsfs_credentials * creds, void * user_data) +{ + bool result = false; + struct args * args = user_data; + + char const * username = wsfs_credentials_get(creds, "username"); + char const * password = wsfs_credentials_get(creds, "password"); + if ((NULL != username) && (NULL != password)) + { + json_t * passwd = json_load_file(args->passwd_path, 0, NULL); + if (NULL != passwd) + { + json_t * user = json_object_get(passwd, username); + if (json_is_object(user)) + { + json_t * password_holder = json_object_get(user, "password"); + if (json_is_string(password_holder)) + { + result = (0 == strcmp(password, json_string_value(password_holder))); + } + } + + json_decref(passwd); + } + } + + return result; +} + static int parse_arguments(int argc, char * argv[], struct args * args) { static struct option const options[] = @@ -47,16 +80,18 @@ static int parse_arguments(int argc, char * argv[], struct args * args) {"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:h", options, &option_index); + int const c = getopt_long(argc, argv, "m:d:c:k:n:p:P:h", options, &option_index); switch (c) { @@ -68,27 +103,31 @@ static int parse_arguments(int argc, char * argv[], struct args * args) finished = true; break; case 'm': - free(args->config.mount_point); - args->config.mount_point = strdup(optarg); + wsfs_server_config_set_mountpoint(args->config, optarg); + has_mountpoint = true; break; case 'd': - free(args->config.document_root); - args->config.document_root = strdup(optarg); + wsfs_server_config_set_documentroot(args->config, optarg); break; case 'c': - free(args->config.cert_path); - args->config.cert_path = strdup(optarg); + wsfs_server_config_set_certpath(args->config, optarg); break; case 'k': - free(args->config.key_path); - args->config.key_path = strdup(optarg); + wsfs_server_config_set_keypath(args->config, optarg); break; case 'n': - free(args->config.vhost_name); - args->config.vhost_name = strdup(optarg); + wsfs_server_config_set_vhostname(args->config, optarg); break; case 'p': - args->config.port = atoi(optarg); + wsfs_server_config_set_port(args->config, atoi(optarg)); + break; + case 'P': + free(args->passwd_path); + args->passwd_path = strdup(optarg); + wsfs_server_config_add_authenticator(args->config, + "username", + &authenticate, + args); break; default: fprintf(stderr, "error: unknown argument\n"); @@ -99,7 +138,7 @@ static int parse_arguments(int argc, char * argv[], struct args * args) if ((EXIT_SUCCESS == result) && (!args->show_help)) { - if (NULL == args->config.mount_point) + if (!has_mountpoint) { fprintf(stderr, "error: missing mount point\n"); result = EXIT_FAILURE; @@ -123,26 +162,19 @@ static void on_interrupt(int signal_id) int main(int argc, char * argv[]) { - struct args args = - { - .config = - { - .mount_point = NULL, - .document_root = NULL, - .cert_path = NULL, - .key_path = NULL, - .vhost_name = strdup("localhost"), - .port = 8080, - }, - .show_help = 0 - }; + struct args args; + args.config = wsfs_server_config_create(); + wsfs_server_config_set_vhostname(args.config, "localhost"); + wsfs_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); - server = wsfs_server_create(&args.config); + server = wsfs_server_create(args.config); if (NULL != server) { wsfs_server_run(server); @@ -159,7 +191,8 @@ int main(int argc, char * argv[]) show_help(); } - wsfs_server_config_cleanup(&args.config); + free(args.passwd_path); + wsfs_server_config_dispose(args.config); return result; } diff --git a/example/lib/userdb/include/userdb.h b/example/lib/userdb/include/userdb.h new file mode 100644 index 0000000..cf133d0 --- /dev/null +++ b/example/lib/userdb/include/userdb.h @@ -0,0 +1,46 @@ +#ifndef USERDB_H +#define USERDB_H + +#ifndef __cplusplus +#include +#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 \ No newline at end of file diff --git a/example/lib/userdb/src/userdb.c b/example/lib/userdb/src/userdb.c new file mode 100644 index 0000000..f25c42b --- /dev/null +++ b/example/lib/userdb/src/userdb.c @@ -0,0 +1,277 @@ +#include "userdb.h" + +#include +#include +#include +#include + +#include +#include + +#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), "wsfs-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("wsfs-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; +} \ No newline at end of file diff --git a/example/passwd/main.c b/example/passwd/main.c new file mode 100644 index 0000000..83ec16e --- /dev/null +++ b/example/passwd/main.c @@ -0,0 +1,304 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + + +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( + "wsfs-passwd, Copyright (c) 2019 authors \n" + "Manage wsfs passwd file\n" + "\n" + "Usage: wsfs-passwd -f -c [-u ] [-p ] [-P ]\n" + "\n" + "Options:\n" + "\t-f, --file Path of wsfs 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: wsfs-passwd -f passwd.json -c create\n" + "\tadd Adds or replaces a user\n" + "\t Example: wsfs-passwd -f passwd.json -c add -u bob -p secret\n" + "\tremove Removes a user\n" + "\t Example: wsfs-passwd -f passwd.json -c remove -u bob\n" + "\tcheck Checks password of a user\n" + "\t Example: wsfs-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 = +{ + "", + &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; +} \ No newline at end of file diff --git a/include/wsfs/adapter/authenticate.h b/include/wsfs/adapter/authenticate.h new file mode 100644 index 0000000..9e630ee --- /dev/null +++ b/include/wsfs/adapter/authenticate.h @@ -0,0 +1,15 @@ +#ifndef WSFS_ADAPTER_AUTHENTICATE_H +#define WSFS_ADAPTER_AUTHENTICATE_H + +#ifndef __cplusplus +#include +#endif + +struct wsfs_credentials; + +typedef bool wsfs_authenticate_fn( + struct wsfs_credentials * credentials, + void * user_data); + + +#endif diff --git a/include/wsfs/adapter/credentials.h b/include/wsfs/adapter/credentials.h new file mode 100644 index 0000000..747b107 --- /dev/null +++ b/include/wsfs/adapter/credentials.h @@ -0,0 +1,25 @@ +#ifndef WSFS_ADAPTER_CREDENTIALS_H +#define WSFS_ADAPTER_CREDENTIALS_H + +#include "wsfs/adapter/api.h" + +struct wsfs_credentials; + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern WSFSA_API char const * wsfs_credentials_type( + struct wsfs_credentials const * credentials); + +extern WSFSA_API char const * wsfs_credentials_get( + struct wsfs_credentials const * credentials, + char const * key); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/include/wsfs/adapter/server_config.h b/include/wsfs/adapter/server_config.h index 27c3bbe..639e1c1 100644 --- a/include/wsfs/adapter/server_config.h +++ b/include/wsfs/adapter/server_config.h @@ -2,31 +2,51 @@ #define WSFS_ADAPTER_SERVER_CONFIG_H #include "wsfs/adapter/api.h" +#include "wsfs/adapter/authenticate.h" -struct wsfs_server_config -{ - char * mount_point; - char * document_root; - char * key_path; - char * cert_path; - char * vhost_name; - int port; -}; +struct wsfs_server_config; #ifdef __cplusplus extern "C" { #endif -extern WSFSA_API void wsfs_server_config_init( - struct wsfs_server_config * config); +extern WSFSA_API struct wsfs_server_config * wsfs_server_config_create(void); -extern WSFSA_API void wsfs_server_config_cleanup( +extern WSFSA_API void wsfs_server_config_dispose( struct wsfs_server_config * config); -extern WSFSA_API void wsfs_server_config_clone( - struct wsfs_server_config * config, - struct wsfs_server_config * clone); + +extern WSFSA_API void wsfs_server_config_set_mountpoint( + struct wsfs_server_config * config, + char const * mount_point); + +extern WSFSA_API void wsfs_server_config_set_documentroot( + struct wsfs_server_config * config, + char const * document_root); + +extern WSFSA_API void wsfs_server_config_set_keypath( + struct wsfs_server_config * config, + char const * key_path); + +extern WSFSA_API void wsfs_server_config_set_certpath( + struct wsfs_server_config * config, + char const * cert_path); + +extern WSFSA_API void wsfs_server_config_set_vhostname( + struct wsfs_server_config * config, + char const * vhost_name); + +extern WSFSA_API void wsfs_server_config_set_port( + struct wsfs_server_config * config, + int port); + +extern WSFSA_API void wsfs_server_config_add_authenticator( + struct wsfs_server_config * config, + char const * type, + wsfs_authenticate_fn * authenticate, + void * user_data +); #ifdef __cplusplus } diff --git a/include/wsfs/adapter/server_protocol.h b/include/wsfs/adapter/server_protocol.h index 1af4cc1..0579677 100644 --- a/include/wsfs/adapter/server_protocol.h +++ b/include/wsfs/adapter/server_protocol.h @@ -1,7 +1,8 @@ #ifndef WSFS_ADAPTER_SERVER_PROTOCOL_H #define WSFS_ADAPTER_SERVER_PROTOCOL_H -#include "wsfs/adapter/api.h" +#include +#include struct wsfs_server_protocol; struct lws_protocols; @@ -21,6 +22,12 @@ extern WSFSA_API void wsfs_server_protocol_init_lws( struct wsfs_server_protocol * protocol, struct lws_protocols * lws_protocol); +extern WSFSA_API void wsfs_server_protocol_add_authenticator( + struct wsfs_server_protocol * protocol, + char const * type, + wsfs_authenticate_fn * authenticate, + void * user_data); + #ifdef __cplusplus } #endif diff --git a/include/wsfs_adapter.h b/include/wsfs_adapter.h index 6a812a3..6900f13 100644 --- a/include/wsfs_adapter.h +++ b/include/wsfs_adapter.h @@ -7,5 +7,7 @@ #include #include #include +#include +#include #endif diff --git a/lib/wsfs/adapter/authenticator.c b/lib/wsfs/adapter/authenticator.c new file mode 100644 index 0000000..04f1c60 --- /dev/null +++ b/lib/wsfs/adapter/authenticator.c @@ -0,0 +1,49 @@ +#include "wsfs/adapter/authenticator.h" + +#include +#include + +#include "wsfs/adapter/credentials_intern.h" + +struct wsfs_authenticator * wsfs_authenticator_create( + char const * type, + wsfs_authenticate_fn * authenticate, + void * user_data) +{ + struct wsfs_authenticator * authenticator = malloc(sizeof(struct wsfs_authenticator)); + if (NULL != authenticator) + { + authenticator->type = strdup(type); + authenticator->authenticate = authenticate; + authenticator->user_data = user_data; + authenticator->next = NULL; + } + + return authenticator; +} + +void wsfs_authenticator_dispose( + struct wsfs_authenticator * authenticator) +{ + free(authenticator->type); + free(authenticator); +} + +bool wsfs_authenticator_autenticate( + struct wsfs_authenticator * authenticator, + struct wsfs_credentials * credentials) +{ + bool result; + + if (0 == strcmp(authenticator->type, credentials->type)) + { + result = authenticator->authenticate(credentials, authenticator->user_data); + } + else + { + result = false; + } + + + return result; +} \ No newline at end of file diff --git a/lib/wsfs/adapter/authenticator.h b/lib/wsfs/adapter/authenticator.h new file mode 100644 index 0000000..585cc24 --- /dev/null +++ b/lib/wsfs/adapter/authenticator.h @@ -0,0 +1,40 @@ +#ifndef WSFS_ADAPTER_AUTHENTICATOR_H +#define WSFS_ADAPTER_AUTHENTICATOR_H + +#ifndef __cplusplus +#include +#endif + +#include "wsfs/adapter/authenticate.h" + +struct wsfs_authenticator +{ + char * type; + wsfs_authenticate_fn * authenticate; + void * user_data; + struct wsfs_authenticator * next; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern struct wsfs_authenticator * wsfs_authenticator_create( + char const * type, + wsfs_authenticate_fn * authenticate, + void * user_data); + +extern void wsfs_authenticator_dispose( + struct wsfs_authenticator * authenticator); + +extern bool wsfs_authenticator_autenticate( + struct wsfs_authenticator * authenticator, + struct wsfs_credentials * credentials); + + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/wsfs/adapter/authenticators.c b/lib/wsfs/adapter/authenticators.c new file mode 100644 index 0000000..856354d --- /dev/null +++ b/lib/wsfs/adapter/authenticators.c @@ -0,0 +1,101 @@ +#include "wsfs/adapter/authenticators.h" +#include +#include + +#include "wsfs/adapter/authenticator.h" +#include "wsfs/adapter/credentials_intern.h" + +static struct wsfs_authenticator * wsfs_authenticators_find( + struct wsfs_authenticators * authenticators, + char const * type) +{ + struct wsfs_authenticator * result = NULL; + + struct wsfs_authenticator * actual = authenticators->first; + while ((NULL == result) && (NULL != actual)) + { + struct wsfs_authenticator * next = actual->next; + if (0 == strcmp(type, actual->type)) + { + result = actual; + } + + actual = next; + } + + return result; +} + +void wsfs_authenticators_init( + struct wsfs_authenticators * authenticators) +{ + authenticators->first = NULL; +} + +void wsfs_authenticators_cleanup( + struct wsfs_authenticators * authenticators) +{ + struct wsfs_authenticator * actual = authenticators->first; + while (NULL != actual) + { + struct wsfs_authenticator * next = actual->next; + wsfs_authenticator_dispose(actual); + actual = next; + } + + authenticators->first = NULL; +} + +void wsfs_authenticators_clone( + struct wsfs_authenticators * authenticators, + struct wsfs_authenticators * other) +{ + wsfs_authenticators_init(other); + + struct wsfs_authenticator * actual = authenticators->first; + while (NULL != actual) + { + struct wsfs_authenticator * next = actual->next; + wsfs_authenticators_add(other, + actual->type, actual->authenticate, actual->user_data); + actual = next; + } + +} + +extern void wsfs_authenticators_move( + struct wsfs_authenticators * authenticators, + struct wsfs_authenticators * other) +{ + other->first = authenticators->first; + authenticators->first = NULL; +} + +void wsfs_authenticators_add( + struct wsfs_authenticators * authenticators, + char const * type, + wsfs_authenticate_fn * authenticate, + void * user_data) +{ + struct wsfs_authenticator * authenticator = wsfs_authenticator_create(type, authenticate, user_data); + authenticator->next = authenticators->first; + authenticators->first = authenticator; +} + +bool wsfs_authenticators_authenticate( + struct wsfs_authenticators * authenticators, + struct wsfs_credentials * credentials) +{ + bool result = (NULL == authenticators->first); + + if (NULL != credentials) + { + struct wsfs_authenticator * authenticator = wsfs_authenticators_find(authenticators, credentials->type); + if (NULL != authenticator) + { + result = wsfs_authenticator_autenticate(authenticator, credentials); + } + } + + return result; +} \ No newline at end of file diff --git a/lib/wsfs/adapter/authenticators.h b/lib/wsfs/adapter/authenticators.h new file mode 100644 index 0000000..48b8056 --- /dev/null +++ b/lib/wsfs/adapter/authenticators.h @@ -0,0 +1,51 @@ +#ifndef WSFS_ADAPTER_AUTHENTICATORS_H +#define WSFS_ADAPTER_AUTHENTICATORS_H + +#ifndef __cplusplus +#include +#endif + +#include "wsfs/adapter/authenticate.h" + +struct wsfs_authenticator; +struct wsfs_credentials; + +struct wsfs_authenticators +{ + struct wsfs_authenticator * first; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wsfs_authenticators_init( + struct wsfs_authenticators * authenticators); + +extern void wsfs_authenticators_cleanup( + struct wsfs_authenticators * authenticators); + +extern void wsfs_authenticators_clone( + struct wsfs_authenticators * authenticators, + struct wsfs_authenticators * other); + +extern void wsfs_authenticators_move( + struct wsfs_authenticators * authenticators, + struct wsfs_authenticators * other); + +extern void wsfs_authenticators_add( + struct wsfs_authenticators * authenticators, + char const * type, + wsfs_authenticate_fn * authenticate, + void * user_data); + +extern bool wsfs_authenticators_authenticate( + struct wsfs_authenticators * authenticators, + struct wsfs_credentials * credentials); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/wsfs/adapter/credentials.c b/lib/wsfs/adapter/credentials.c new file mode 100644 index 0000000..fa8b09e --- /dev/null +++ b/lib/wsfs/adapter/credentials.c @@ -0,0 +1,40 @@ +#include "wsfs/adapter/credentials_intern.h" +#include + +void wsfs_credentials_init( + struct wsfs_credentials * credentials, + char const * type, + json_t * data) +{ + credentials->type = strdup(type); + credentials->data = data; + json_incref(credentials->data); +} + +void wsfs_credentials_cleanup( + struct wsfs_credentials * credentials) +{ + free(credentials->type); + json_decref(credentials->data); +} + +char const * wsfs_credentials_type( + struct wsfs_credentials const * credentials) +{ + return credentials->type; +} + +char const * wsfs_credentials_get( + struct wsfs_credentials const * credentials, + char const * key) +{ + char const * result = NULL; + + json_t * value_holder = json_object_get(credentials->data, key); + if (json_is_string(value_holder)) + { + result = json_string_value(value_holder); + } + + return result; +} diff --git a/lib/wsfs/adapter/credentials_intern.h b/lib/wsfs/adapter/credentials_intern.h new file mode 100644 index 0000000..026c515 --- /dev/null +++ b/lib/wsfs/adapter/credentials_intern.h @@ -0,0 +1,30 @@ +#ifndef WSFS_ADAPTER_CREDENTIALS_INTERN_H +#define WSFS_ADAPTER_CREDENTIALS_INTERN_H + +#include "wsfs/adapter/credentials.h" +#include + +struct wsfs_credentials +{ + char * type; + json_t * data; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wsfs_credentials_init( + struct wsfs_credentials * credentials, + char const * type, + json_t * data); + +extern void wsfs_credentials_cleanup( + struct wsfs_credentials * credentials); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/lib/wsfs/adapter/server.c b/lib/wsfs/adapter/server.c index 2590f0d..0ca0c74 100644 --- a/lib/wsfs/adapter/server.c +++ b/lib/wsfs/adapter/server.c @@ -4,7 +4,11 @@ #include #include -#include "wsfs/adapter/server_config.h" +#include +#include +#include + +#include "wsfs/adapter/server_config_intern.h" #include "wsfs/adapter/server_protocol_intern.h" #define WSFS_DISABLE_LWS_LOG 0 @@ -74,24 +78,50 @@ static struct lws_context * wsfs_server_context_create( } -struct wsfs_server * wsfs_server_create( - struct wsfs_server_config * config) +static bool wsfs_server_check_mountpoint( + struct wsfs_server_config * config) { - struct wsfs_server * server = malloc(sizeof(struct wsfs_server)); - if (NULL != server) - { - if (wsfs_server_protocol_init(&server->protocol, config->mount_point)) + bool result = false; + + if (NULL != config->mount_point) + { + struct stat info; + int const rc = stat(config->mount_point, &info); + result = ((0 == rc) && (S_ISDIR(info.st_mode))); + + if (!result) { - server->shutdown_requested = false; - wsfs_server_config_clone(config, &server->config); - server->context = wsfs_server_context_create(server); + result = (0 == mkdir(config->mount_point, 0755)); } - else + } + + return result; +} + +struct wsfs_server * wsfs_server_create( + struct wsfs_server_config * config) +{ + struct wsfs_server * server = NULL; + + if (wsfs_server_check_mountpoint(config)) + { + server = malloc(sizeof(struct wsfs_server)); + if (NULL != server) { - free(server); - server = NULL; - } - } + if (wsfs_server_protocol_init(&server->protocol, config->mount_point)) + { + server->shutdown_requested = false; + wsfs_server_config_clone(config, &server->config); + wsfs_authenticators_move(&server->config.authenticators, &server->protocol.authenticators); + server->context = wsfs_server_context_create(server); + } + else + { + free(server); + server = NULL; + } + } + } return server; } diff --git a/lib/wsfs/adapter/server_config.c b/lib/wsfs/adapter/server_config.c index 8f88a17..3eee3db 100644 --- a/lib/wsfs/adapter/server_config.c +++ b/lib/wsfs/adapter/server_config.c @@ -1,4 +1,4 @@ -#include "wsfs/adapter/server_config.h" +#include "wsfs/adapter/server_config_intern.h" #include #include @@ -18,11 +18,15 @@ void wsfs_server_config_init( struct wsfs_server_config * config) { memset(config, 0, sizeof(struct wsfs_server_config)); + + wsfs_authenticators_init(&config->authenticators); } void wsfs_server_config_cleanup( struct wsfs_server_config * config) { + wsfs_authenticators_cleanup(&config->authenticators); + free(config->mount_point); free(config->document_root); free(config->key_path); @@ -42,4 +46,82 @@ void wsfs_server_config_clone( clone->cert_path = wsfs_server_config_strdup(config->cert_path); clone->vhost_name = wsfs_server_config_strdup(config->vhost_name); clone->port = config->port; + + wsfs_authenticators_clone(&config->authenticators, &clone->authenticators); +} + +struct wsfs_server_config * wsfs_server_config_create(void) +{ + struct wsfs_server_config * config = malloc(sizeof(struct wsfs_server_config)); + if (NULL != config) + { + wsfs_server_config_init(config); + } + + return config; +} + +void wsfs_server_config_dispose( + struct wsfs_server_config * config) +{ + wsfs_server_config_cleanup(config); + free(config); +} + +void wsfs_server_config_set_mountpoint( + struct wsfs_server_config * config, + char const * mount_point) +{ + free(config->mount_point); + config->mount_point = strdup(mount_point); +} + +void wsfs_server_config_set_documentroot( + struct wsfs_server_config * config, + char const * document_root) +{ + free(config->document_root); + config->document_root = strdup(document_root); +} + +void wsfs_server_config_set_keypath( + struct wsfs_server_config * config, + char const * key_path) +{ + free(config->key_path); + config->key_path = strdup(key_path); +} + +void wsfs_server_config_set_certpath( + struct wsfs_server_config * config, + char const * cert_path) +{ + free(config->cert_path); + config->cert_path = strdup(cert_path); +} + +void wsfs_server_config_set_vhostname( + struct wsfs_server_config * config, + char const * vhost_name) +{ + free(config->vhost_name); + config->vhost_name = strdup(vhost_name); +} + +void wsfs_server_config_set_port( + struct wsfs_server_config * config, + int port) +{ + config->port = port; } + +void wsfs_server_config_add_authenticator( + struct wsfs_server_config * config, + char const * type, + wsfs_authenticate_fn * authenticate, + void * user_data +) +{ + wsfs_authenticators_add(&config->authenticators, type, authenticate, user_data); +} + diff --git a/lib/wsfs/adapter/server_config_intern.h b/lib/wsfs/adapter/server_config_intern.h new file mode 100644 index 0000000..2ae0a2f --- /dev/null +++ b/lib/wsfs/adapter/server_config_intern.h @@ -0,0 +1,37 @@ +#ifndef WSFS_ADAPTER_SERVER_CONFIG_INTERN_H +#define WSFS_ADAPTER_SERVER_CONFIG_INTERN_H + +#include "wsfs/adapter/server_config.h" +#include "wsfs/adapter/authenticators.h" + +struct wsfs_server_config +{ + char * mount_point; + char * document_root; + char * key_path; + char * cert_path; + char * vhost_name; + int port; + struct wsfs_authenticators authenticators; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void wsfs_server_config_init( + struct wsfs_server_config * config); + +extern void wsfs_server_config_cleanup( + struct wsfs_server_config * config); + +extern void wsfs_server_config_clone( + struct wsfs_server_config * config, + struct wsfs_server_config * clone); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/wsfs/adapter/server_protocol.c b/lib/wsfs/adapter/server_protocol.c index fa53e0d..2d037c4 100644 --- a/lib/wsfs/adapter/server_protocol.c +++ b/lib/wsfs/adapter/server_protocol.c @@ -19,6 +19,7 @@ static int wsfs_server_protocol_callback( struct wsfs_server_protocol * protocol = ws_protocol->user; wsfs_timeout_manager_check(&protocol->timeout_manager); + struct wsfs_session * session = wsfs_session_manager_get(&protocol->session_manager, wsi); switch (reason) { @@ -33,33 +34,31 @@ static int wsfs_server_protocol_callback( } break; case LWS_CALLBACK_ESTABLISHED: - if (NULL == protocol->wsi) - { - protocol->wsi = wsi; - } - break; - case LWS_CALLBACK_CLOSED: - if (wsi == protocol->wsi) + session = wsfs_session_manager_add( + &protocol->session_manager, + wsi, + &protocol->authenticators, + &protocol->rpc); + + if (NULL != session) { - protocol->wsi = NULL; - wsfs_message_queue_cleanup(&protocol->queue); + wsfs_session_authenticate(session, NULL); } + break; + case LWS_CALLBACK_CLOSED: + wsfs_session_manager_remove(&protocol->session_manager, wsi); break; case LWS_CALLBACK_SERVER_WRITEABLE: - if ((wsi == protocol->wsi) && (!wsfs_message_queue_empty(&protocol->queue))) + if (NULL != session) { - struct wsfs_message * message = wsfs_message_queue_pop(&protocol->queue); - lws_write(wsi, (unsigned char*) message->data, message->length, LWS_WRITE_TEXT); - wsfs_message_dispose(message); - - if (!wsfs_message_queue_empty(&protocol->queue)) - { - lws_callback_on_writable(wsi); - } + wsfs_session_onwritable(session); } break; case LWS_CALLBACK_RECEIVE: - wsfs_jsonrpc_server_onresult(&protocol->rpc, in, len); + if (NULL != session) + { + wsfs_session_receive(session, in, len); + } break; case LWS_CALLBACK_RAW_RX_FILE: wsfs_filesystem_process_request(&protocol->filesystem); @@ -75,20 +74,11 @@ static bool wsfs_server_protocol_invoke( void * user_data, json_t const * request) { - bool result = false; struct wsfs_server_protocol * protocol = user_data; + struct wsfs_session * session = &protocol->session_manager.session; + struct wsfs_message * message = wsfs_message_create(request); - if (NULL != protocol->wsi) - { - struct wsfs_message * message = wsfs_message_create(request); - if (NULL != message) - { - wsfs_message_queue_push(&protocol->queue, message); - lws_callback_on_writable(protocol->wsi); - - result = true; - } - } + bool const result = wsfs_session_send(session, message); return result; } @@ -130,10 +120,9 @@ bool wsfs_server_protocol_init( struct wsfs_server_protocol * protocol, char * mount_point) { - protocol->wsi = NULL; - wsfs_message_queue_init(&protocol->queue); - wsfs_timeout_manager_init(&protocol->timeout_manager); + wsfs_session_manager_init(&protocol->session_manager); + wsfs_authenticators_init(&protocol->authenticators); wsfs_jsonrpc_server_init(&protocol->rpc, &protocol->timeout_manager); wsfs_jsonrpc_server_add(&protocol->rpc, "lookup", &wsfs_server_protocol_invoke, protocol); @@ -149,8 +138,9 @@ bool wsfs_server_protocol_init( if (!success) { wsfs_jsonrpc_server_cleanup(&protocol->rpc); + wsfs_authenticators_cleanup(&protocol->authenticators); wsfs_timeout_manager_cleanup(&protocol->timeout_manager); - wsfs_message_queue_cleanup(&protocol->queue); + wsfs_session_manager_cleanup(&protocol->session_manager); } return success; @@ -162,6 +152,15 @@ void wsfs_server_protocol_cleanup( wsfs_filesystem_cleanup(&protocol->filesystem); wsfs_jsonrpc_server_cleanup(&protocol->rpc); wsfs_timeout_manager_cleanup(&protocol->timeout_manager); - wsfs_message_queue_cleanup(&protocol->queue); - protocol->wsi = NULL; + wsfs_authenticators_cleanup(&protocol->authenticators); + wsfs_session_manager_cleanup(&protocol->session_manager); +} + +void wsfs_server_protocol_add_authenticator( + struct wsfs_server_protocol * protocol, + char const * type, + wsfs_authenticate_fn * authenticate, + void * user_data) +{ + wsfs_authenticators_add(&protocol->authenticators, type, authenticate, user_data); } diff --git a/lib/wsfs/adapter/server_protocol_intern.h b/lib/wsfs/adapter/server_protocol_intern.h index ecdb0e9..7304934 100644 --- a/lib/wsfs/adapter/server_protocol_intern.h +++ b/lib/wsfs/adapter/server_protocol_intern.h @@ -1,20 +1,20 @@ #ifndef WSFS_ADAPTER_SERVER_PROTOCOL_INTERN_H #define WSFS_ADAPTER_SERVER_PROTOCOL_INTERN_H -#include "wsfs/message_queue.h" - #include "wsfs/adapter/server_protocol.h" #include "wsfs/adapter/filesystem.h" #include "wsfs/adapter/jsonrpc/server.h" #include "wsfs/adapter/time/timeout_manager.h" +#include "wsfs/adapter/authenticators.h" +#include "wsfs/adapter/session_manager.h" struct wsfs_server_protocol { struct wsfs_timeout_manager timeout_manager; struct wsfs_filesystem filesystem; struct wsfs_jsonrpc_server rpc; - struct wsfs_message_queue queue; - struct lws * wsi; + struct wsfs_authenticators authenticators; + struct wsfs_session_manager session_manager; }; extern bool wsfs_server_protocol_init( diff --git a/lib/wsfs/adapter/session.c b/lib/wsfs/adapter/session.c new file mode 100644 index 0000000..1112e64 --- /dev/null +++ b/lib/wsfs/adapter/session.c @@ -0,0 +1,84 @@ +#include "wsfs/adapter/session.h" +#include "wsfs/adapter/authenticators.h" +#include "wsfs/message_queue.h" +#include "wsfs/message.h" +#include "wsfs/adapter/jsonrpc/server.h" + +#include +#include + +void wsfs_session_init( + struct wsfs_session * session, + struct lws * wsi, + struct wsfs_authenticators * authenticators, + struct wsfs_jsonrpc_server * rpc) + { + session->wsi = wsi; + session->is_authenticated = false; + session->authenticators = authenticators; + session->rpc = rpc; + wsfs_message_queue_init(&session->queue); + } + +void wsfs_session_cleanup( + struct wsfs_session * session) +{ + wsfs_message_queue_cleanup(&session->queue); + session->is_authenticated = false; + session->wsi = NULL; + session->authenticators = NULL; + session->rpc = NULL; +} + +void wsfs_session_authenticate( + struct wsfs_session * session, + struct wsfs_credentials * creds) +{ + session->is_authenticated = wsfs_authenticators_authenticate(session->authenticators, creds); +} + +bool wsfs_session_send( + struct wsfs_session * session, + struct wsfs_message * message) +{ + bool result = (session->is_authenticated) && (NULL != session->wsi); + + if (result) + { + wsfs_message_queue_push(&session->queue, message); + lws_callback_on_writable(session->wsi); + + result = true; + } + else + { + wsfs_message_dispose(message); + } + + return result; +} + +void wsfs_session_onwritable( + struct wsfs_session * session) +{ + if (!wsfs_message_queue_empty(&session->queue)) + { + struct wsfs_message * message = wsfs_message_queue_pop(&session->queue); + lws_write(session->wsi, (unsigned char*) message->data, message->length, LWS_WRITE_TEXT); + wsfs_message_dispose(message); + + if (!wsfs_message_queue_empty(&session->queue)) + { + lws_callback_on_writable(session->wsi); + } + } +} + + +void wsfs_session_receive( + struct wsfs_session * session, + char const * data, + size_t length) +{ + wsfs_jsonrpc_server_onresult(session->rpc, data, length); +} \ No newline at end of file diff --git a/lib/wsfs/adapter/session.h b/lib/wsfs/adapter/session.h new file mode 100644 index 0000000..86d41d8 --- /dev/null +++ b/lib/wsfs/adapter/session.h @@ -0,0 +1,64 @@ +#ifndef WSFS_ADAPTER_SESSION_H +#define WSFS_ADAPTER_SESSION_H + +#ifndef __cplusplus +#include +#include +#else +#include +using std::size_t; +#endif + +#include "wsfs/message_queue.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct lws; +struct wsfs_message; +struct wsfs_credentials; +struct wsfs_authenticators; +struct wsfs_jsonrpc_server; + +struct wsfs_session +{ + struct lws * wsi; + bool is_authenticated; + struct wsfs_message_queue queue; + struct wsfs_authenticators * authenticators; + struct wsfs_jsonrpc_server * rpc; +}; + +extern void wsfs_session_init( + struct wsfs_session * session, + struct lws * wsi, + struct wsfs_authenticators * authenticators, + struct wsfs_jsonrpc_server * rpc); + +extern void wsfs_session_authenticate( + struct wsfs_session * session, + struct wsfs_credentials * creds); + +extern bool wsfs_session_send( + struct wsfs_session * session, + struct wsfs_message * message); + +extern void wsfs_session_receive( + struct wsfs_session * session, + char const * data, + size_t length); + +extern void wsfs_session_onwritable( + struct wsfs_session * session); + +extern void wsfs_session_cleanup( + struct wsfs_session * session); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/lib/wsfs/adapter/session_manager.c b/lib/wsfs/adapter/session_manager.c new file mode 100644 index 0000000..290398e --- /dev/null +++ b/lib/wsfs/adapter/session_manager.c @@ -0,0 +1,54 @@ +#include "wsfs/adapter/session_manager.h" +#include + +void wsfs_session_manager_init( + struct wsfs_session_manager * manager) +{ + wsfs_session_init(&manager->session, NULL, NULL, NULL); +} + +void wsfs_session_manager_cleanup( + struct wsfs_session_manager * manager) +{ + wsfs_session_cleanup(&manager->session); +} + +struct wsfs_session * wsfs_session_manager_add( + struct wsfs_session_manager * manager, + struct lws * wsi, + struct wsfs_authenticators * authenticators, + struct wsfs_jsonrpc_server * rpc) +{ + struct wsfs_session * session = NULL; + if (NULL == manager->session.wsi) + { + session = &manager->session; + wsfs_session_init(&manager->session, wsi, authenticators, rpc); + } + + return session; +} + +struct wsfs_session * wsfs_session_manager_get( + struct wsfs_session_manager * manager, + struct lws * wsi) +{ + struct wsfs_session * session = NULL; + if (wsi == manager->session.wsi) + { + session = &manager->session; + } + + return session; +} + +void wsfs_session_manager_remove( + struct wsfs_session_manager * manager, + struct lws * wsi) +{ + if (wsi == manager->session.wsi) + { + wsfs_session_cleanup(&manager->session); + manager->session.wsi = NULL; + } +} diff --git a/lib/wsfs/adapter/session_manager.h b/lib/wsfs/adapter/session_manager.h new file mode 100644 index 0000000..d047aef --- /dev/null +++ b/lib/wsfs/adapter/session_manager.h @@ -0,0 +1,46 @@ +#ifndef WSFS_ADAPTER_SESSION_MANAGER_H +#define WSFS_ADAPTER_SESSION_MANAGER_H + +#ifndef __cplusplus +#include +#endif + +#include "wsfs/adapter/session.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct lws; + +struct wsfs_session_manager +{ + struct wsfs_session session; +}; + +extern void wsfs_session_manager_init( + struct wsfs_session_manager * manager); + +extern void wsfs_session_manager_cleanup( + struct wsfs_session_manager * manager); + +extern struct wsfs_session * wsfs_session_manager_add( + struct wsfs_session_manager * manager, + struct lws * wsi, + struct wsfs_authenticators * authenticators, + struct wsfs_jsonrpc_server * rpc); + +extern struct wsfs_session * wsfs_session_manager_get( + struct wsfs_session_manager * manager, + struct lws * wsi); + +extern void wsfs_session_manager_remove( + struct wsfs_session_manager * manager, + struct lws * wsi); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/lib/wsfs/provider/operation/getattr.c b/lib/wsfs/provider/operation/getattr.c index c4be838..90aefa0 100644 --- a/lib/wsfs/provider/operation/getattr.c +++ b/lib/wsfs/provider/operation/getattr.c @@ -32,7 +32,7 @@ void wsfsp_getattr_default( ino_t WSFS_UNUSED_PARAM(inode), void * WSFS_UNUSED_PARAM(user_data)) { - wsfsp_respond_error(request, -1); + wsfsp_respond_error(request, WSFS_BAD_NOENTRY); } void wsfsp_respond_getattr( diff --git a/test/mock_authenticator.cc b/test/mock_authenticator.cc new file mode 100644 index 0000000..f455270 --- /dev/null +++ b/test/mock_authenticator.cc @@ -0,0 +1,42 @@ +#include "mock_authenticator.hpp" + +#define WSFS_AUTHENTICTOR_COUNT 3 + +namespace +{ + +wsfs_test::Authenticator * g_authenticators[WSFS_AUTHENTICTOR_COUNT]; + +} + + +namespace wsfs_test +{ + +void set_authenticator(Authenticator * authenticator) +{ + set_authenticator(0, authenticator); +} + +void set_authenticator(size_t i, Authenticator * authenticator) +{ + g_authenticators[i] = authenticator; +} + +bool authenticate(struct wsfs_credentials * creds, void * user_data) +{ + return g_authenticators[0]->authenticate(creds, user_data); +} + +bool authenticate_1(struct wsfs_credentials * creds, void * user_data) +{ + return g_authenticators[1]->authenticate(creds, user_data); +} + +bool authenticate_2(struct wsfs_credentials * creds, void * user_data) +{ + return g_authenticators[2]->authenticate(creds, user_data); +} + + +} \ No newline at end of file diff --git a/test/mock_authenticator.hpp b/test/mock_authenticator.hpp new file mode 100644 index 0000000..ef5d909 --- /dev/null +++ b/test/mock_authenticator.hpp @@ -0,0 +1,34 @@ +#ifndef MOCK_AUTHENTICATOR_H +#define MOCK_AUTHENTICATOR_H + +#include +#include "wsfs/adapter/authenticator.h" + +namespace wsfs_test +{ + +class Authenticator +{ +public: + virtual ~Authenticator() { } + virtual bool authenticate( + struct wsfs_credentials * credentials, + void * user_data) = 0; +}; + +class MockAuthenticator: public Authenticator +{ +public: + MOCK_METHOD2(authenticate, bool (struct wsfs_credentials * credentials, void * user_data)); +}; + +void set_authenticator(Authenticator * authenticator); +void set_authenticator(size_t index, Authenticator * authenticator); + +bool authenticate(struct wsfs_credentials * creds, void * user_data); +bool authenticate_1(struct wsfs_credentials * creds, void * user_data); +bool authenticate_2(struct wsfs_credentials * creds, void * user_data); + +} + +#endif diff --git a/test/test_authenticator.cc b/test/test_authenticator.cc new file mode 100644 index 0000000..71989f2 --- /dev/null +++ b/test/test_authenticator.cc @@ -0,0 +1,63 @@ +#include +#include + +#include "mock_authenticator.hpp" + +#include "wsfs/adapter/authenticator.h" +#include "wsfs/adapter/credentials_intern.h" + +using ::testing::Return; +using ::testing::_; +using ::wsfs_test::Authenticator; +using ::wsfs_test::MockAuthenticator; +using ::wsfs_test::set_authenticator; +using ::wsfs_test::authenticate; + + +TEST(Authenticator, Authenticate) +{ + MockAuthenticator mock; + set_authenticator(&mock); + + struct wsfs_credentials creds; + wsfs_credentials_init(&creds, "username", nullptr); + char dummy[] = "usr_data"; + void * user_data = reinterpret_cast(dummy); + + EXPECT_CALL(mock, authenticate(&creds, user_data)) + .Times(1) + .WillRepeatedly(Return(true)); + + struct wsfs_authenticator * authenticator = wsfs_authenticator_create( + "username", + &authenticate, + user_data); + + bool result = wsfs_authenticator_autenticate(authenticator, &creds); + ASSERT_TRUE(result); + + wsfs_authenticator_dispose(authenticator); + wsfs_credentials_cleanup(&creds); +} + +TEST(Authenticator, SkipAuthenticationWithWrongType) +{ + MockAuthenticator mock; + set_authenticator(&mock); + + struct wsfs_credentials creds; + wsfs_credentials_init(&creds, "username", nullptr); + EXPECT_CALL(mock, authenticate(_, _)) + .Times(0); + + struct wsfs_authenticator * authenticator = wsfs_authenticator_create( + "certificate", + &authenticate, + nullptr); + + bool result = wsfs_authenticator_autenticate(authenticator, &creds); + ASSERT_FALSE(result); + + wsfs_authenticator_dispose(authenticator); + wsfs_credentials_cleanup(&creds); +} \ No newline at end of file diff --git a/test/test_authenticators.cc b/test/test_authenticators.cc new file mode 100644 index 0000000..6c7c021 --- /dev/null +++ b/test/test_authenticators.cc @@ -0,0 +1,154 @@ +#include +#include + +#include "wsfs/adapter/authenticators.h" +#include "wsfs/adapter/credentials_intern.h" +#include "mock_authenticator.hpp" + +using ::testing::_; +using ::testing::Return; +using ::wsfs_test::MockAuthenticator; +using ::wsfs_test::set_authenticator; +using ::wsfs_test::authenticate; +using ::wsfs_test::authenticate_1; +using ::wsfs_test::authenticate_2; + + +TEST(Authenticators, CloneEmpty) +{ + struct wsfs_authenticators authenticators; + struct wsfs_authenticators clone; + + wsfs_authenticators_init(&authenticators); + ASSERT_EQ(nullptr, authenticators.first); + + wsfs_authenticators_clone(&authenticators, &clone); + ASSERT_EQ(nullptr, clone.first); + + wsfs_authenticators_cleanup(&authenticators); + wsfs_authenticators_cleanup(&clone); +} + +TEST(Authenticators, Clone) +{ + struct wsfs_authenticators authenticators; + struct wsfs_authenticators clone; + + wsfs_authenticators_init(&authenticators); + wsfs_authenticators_add(&authenticators, "username", &authenticate, nullptr); + ASSERT_NE(nullptr, authenticators.first); + + wsfs_authenticators_clone(&authenticators, &clone); + ASSERT_NE(nullptr, clone.first); + ASSERT_NE(nullptr, authenticators.first); + ASSERT_NE(authenticators.first, clone.first); + + wsfs_authenticators_cleanup(&authenticators); + wsfs_authenticators_cleanup(&clone); +} + +TEST(Authenticators, Move) +{ + struct wsfs_authenticators authenticators; + struct wsfs_authenticators clone; + + wsfs_authenticators_init(&authenticators); + wsfs_authenticators_add(&authenticators, "username", &authenticate, nullptr); + ASSERT_NE(nullptr, authenticators.first); + + wsfs_authenticators_move(&authenticators, &clone); + ASSERT_NE(nullptr, clone.first); + ASSERT_EQ(nullptr, authenticators.first); + ASSERT_NE(authenticators.first, clone.first); + + wsfs_authenticators_cleanup(&authenticators); + wsfs_authenticators_cleanup(&clone); +} + +TEST(Authenticators, AuthenticateWithoutAuthenticators) +{ + struct wsfs_credentials creds; + wsfs_credentials_init(&creds, "username", nullptr); + + struct wsfs_authenticators authenticators; + wsfs_authenticators_init(&authenticators); + + bool result = wsfs_authenticators_authenticate(&authenticators, &creds); + ASSERT_TRUE(result); + + result = wsfs_authenticators_authenticate(&authenticators, nullptr); + ASSERT_TRUE(result); + + wsfs_authenticators_cleanup(&authenticators); + wsfs_credentials_cleanup(&creds); +} + +TEST(Authenticators, FailToAuthenticateWithoutCredentials) +{ + MockAuthenticator mock; + set_authenticator(&mock); + + struct wsfs_authenticators authenticators; + wsfs_authenticators_init(&authenticators); + wsfs_authenticators_add(&authenticators, "username", &authenticate, nullptr); + + bool result = wsfs_authenticators_authenticate(&authenticators, nullptr); + ASSERT_FALSE(result); + + wsfs_authenticators_cleanup(&authenticators); +} + +TEST(Authenticators, AuthenticateWithMultipleCredentials) +{ + struct wsfs_credentials creds; + wsfs_credentials_init(&creds, "username", nullptr); + + MockAuthenticator username_mock; + set_authenticator(1, &username_mock); + EXPECT_CALL(username_mock, authenticate(&creds, nullptr)) + .Times(1) + .WillRepeatedly(Return(true)); + + MockAuthenticator certificate_mock; + set_authenticator(2, &certificate_mock); + EXPECT_CALL(certificate_mock, authenticate(_, _)) + .Times(0); + + struct wsfs_authenticators authenticators; + wsfs_authenticators_init(&authenticators); + wsfs_authenticators_add(&authenticators, "username", &authenticate_1, nullptr); + wsfs_authenticators_add(&authenticators, "certificate", &authenticate_2, nullptr); + + bool result = wsfs_authenticators_authenticate(&authenticators, &creds); + ASSERT_TRUE(result); + + wsfs_authenticators_cleanup(&authenticators); + wsfs_credentials_cleanup(&creds); +} + +TEST(Authenticators, FailedAuthenticateWithWrongType) +{ + struct wsfs_credentials creds; + wsfs_credentials_init(&creds, "token", nullptr); + + MockAuthenticator username_mock; + set_authenticator(1, &username_mock); + EXPECT_CALL(username_mock, authenticate(&creds, nullptr)) + .Times(0); + + MockAuthenticator certificate_mock; + set_authenticator(2, &certificate_mock); + EXPECT_CALL(certificate_mock, authenticate(_, _)) + .Times(0); + + struct wsfs_authenticators authenticators; + wsfs_authenticators_init(&authenticators); + wsfs_authenticators_add(&authenticators, "username", &authenticate_1, nullptr); + wsfs_authenticators_add(&authenticators, "certificate", &authenticate_2, nullptr); + + bool result = wsfs_authenticators_authenticate(&authenticators, &creds); + ASSERT_FALSE(result); + + wsfs_authenticators_cleanup(&authenticators); + wsfs_credentials_cleanup(&creds); +} diff --git a/test/test_credentials.cc b/test/test_credentials.cc new file mode 100644 index 0000000..9862d78 --- /dev/null +++ b/test/test_credentials.cc @@ -0,0 +1,70 @@ +#include + +#include "wsfs/adapter/credentials_intern.h" +#include + +TEST(Credentials, Type) +{ + struct wsfs_credentials creds; + + wsfs_credentials_init(&creds, "test", nullptr); + ASSERT_STREQ("test", wsfs_credentials_type(&creds)); + wsfs_credentials_cleanup(&creds); +} + +TEST(Credentials, Get) +{ + struct wsfs_credentials creds; + json_t * data = json_object(); + json_object_set_new(data, "username", json_string("bob")); + json_object_set_new(data, "password", json_string("")); + + wsfs_credentials_init(&creds, "username", data); + ASSERT_STREQ("username", wsfs_credentials_type(&creds)); + ASSERT_STREQ("bob", wsfs_credentials_get(&creds, "username")); + ASSERT_STREQ("", wsfs_credentials_get(&creds, "password")); + + wsfs_credentials_cleanup(&creds); + json_decref(data); +} + +TEST(Credentials, FailedToGetNonexistingValue) +{ + struct wsfs_credentials creds; + json_t * data = json_object(); + + wsfs_credentials_init(&creds, "username", data); + ASSERT_STREQ("username", wsfs_credentials_type(&creds)); + ASSERT_STREQ(nullptr, wsfs_credentials_get(&creds, "username")); + ASSERT_STREQ(nullptr, wsfs_credentials_get(&creds, "password")); + + wsfs_credentials_cleanup(&creds); + json_decref(data); +} + +TEST(Credentials, FailedToGetWithoutData) +{ + struct wsfs_credentials creds; + + wsfs_credentials_init(&creds, "username", nullptr); + ASSERT_STREQ("username", wsfs_credentials_type(&creds)); + ASSERT_STREQ(nullptr, wsfs_credentials_get(&creds, "username")); + ASSERT_STREQ(nullptr, wsfs_credentials_get(&creds, "password")); + + wsfs_credentials_cleanup(&creds); +} + +TEST(Credentials, FailedToGetWrongDataType) +{ + struct wsfs_credentials creds; + json_t * data = json_string("invalid_creds"); + + wsfs_credentials_init(&creds, "username", data); + ASSERT_STREQ("username", wsfs_credentials_type(&creds)); + ASSERT_STREQ(nullptr, wsfs_credentials_get(&creds, "username")); + ASSERT_STREQ(nullptr, wsfs_credentials_get(&creds, "password")); + + wsfs_credentials_cleanup(&creds); + json_decref(data); +} + diff --git a/test/test_server.cc b/test/test_server.cc index 965852e..a5625c5 100644 --- a/test/test_server.cc +++ b/test/test_server.cc @@ -13,12 +13,13 @@ TEST(server, create_dispose) { mkdir("test", 0700); - struct wsfs_server_config config = {strdup("test"), nullptr, nullptr, nullptr, nullptr, 0}; - struct wsfs_server * server = wsfs_server_create(&config); + struct wsfs_server_config * config = wsfs_server_config_create(); + wsfs_server_config_set_mountpoint(config, "test"); + struct wsfs_server * server = wsfs_server_create(config); ASSERT_NE(nullptr, server); wsfs_server_dispose(server); - wsfs_server_config_cleanup(&config); + wsfs_server_config_dispose(config); rmdir("test"); } \ No newline at end of file