1
0
mirror of https://github.com/falk-werner/webfuse synced 2025-06-13 12:54:15 +00:00

added userdb to encapsulate authentication stuff

This commit is contained in:
Falk Werner 2019-03-20 21:46:36 +01:00
parent 8e3ec7bf1a
commit 988af6c2b9
4 changed files with 390 additions and 103 deletions

View File

@ -159,6 +159,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
@ -191,12 +209,14 @@ add_executable(wsfs-passwd
)
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}
)

View 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

View File

@ -0,0 +1,278 @@
#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), "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 = digest->md_size;
unsigned char * hash = malloc(hash_size);
if (NULL != hash)
{
EVP_MD_CTX context;
EVP_MD_CTX_init(&context);
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_cleanup(&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;
}

View File

@ -11,10 +11,8 @@
#include <openssl/engine.h>
#include <openssl/evp.h>
#include <jansson.h>
#include <userdb.h>
#define HASH_ALGORITHM "sha512"
#define PASSWD_FORMAT_MAJOR 1
#define PASSWD_FORMAT_MINOR 0
struct args
{
@ -151,22 +149,11 @@ static void args_init(struct args * args)
static int create_passwd(struct args * args)
{
json_t * db = json_object();
struct userdb * db = userdb_create(args->pepper);
bool result = userdb_save(db, args->file);
userdb_dispose(db);
json_t * meta = json_object();
json_object_set_new(meta, "type", json_string("wsfs-passwd"));
json_object_set_new(meta, "major", json_integer(PASSWD_FORMAT_MAJOR));
json_object_set_new(meta, "minor", json_integer(PASSWD_FORMAT_MINOR));
json_object_set_new(meta, "hash_alorithm", json_string(HASH_ALGORITHM));
json_object_set_new(db, "meta", meta);
json_t * users = json_object();
json_object_set_new(db, "users", users);
int result = json_dump_file(db, args->file, JSON_INDENT(2));
json_decref(db);
return (0 == result) ? EXIT_SUCCESS : EXIT_FAILURE;
return (result) ? EXIT_SUCCESS : EXIT_FAILURE;
}
static int add_user(struct args * args)
@ -185,22 +172,56 @@ static int add_user(struct args * args)
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);
puts("add");
return EXIT_FAILURE;
return (result) ? EXIT_SUCCESS : EXIT_FAILURE;
}
static int remove_user(struct args * args)
{
puts("remove");
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)
{
puts("check");
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)
@ -246,84 +267,6 @@ static void args_cleanup(struct args * args)
free(args->pepper);
}
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 * get_password_hash(
char const * password,
char const * salt,
char * pepper)
{
EVP_MD const * digest = EVP_get_digestbyname(HASH_ALGORITHM);
if (NULL == digest)
{
fprintf(stderr, "error: hash algorithm %s not supported\n", HASH_ALGORITHM);
exit(EXIT_FAILURE);
}
char * result = NULL;
unsigned int hash_size = digest->md_size;
unsigned char * hash = malloc(hash_size);
if (NULL != hash)
{
EVP_MD_CTX context;
EVP_MD_CTX_init(&context);
EVP_DigestInit_ex(&context, digest, NULL);
EVP_DigestUpdate(&context, password, strlen(password));
EVP_DigestUpdate(&context, salt, strlen(salt));
EVP_DigestUpdate(&context, pepper, strlen(pepper));
EVP_DigestFinal_ex(&context, hash, &hash_size);
EVP_MD_CTX_cleanup(&context);
result = to_hex(hash, hash_size);
free(hash);
}
return result;
}
static void openssl_cleanup(void)
{
FIPS_mode_set(0);